`, ``, ``, and `` may be collapsed to single spaces. Move code samples out of the file before translation if line breaks matter.
* Topics with dense inline-element markup (combinations of ``, ``, ``, nested ``) may fail with an internal error — simplify or split the topic.
**JSON**
* Only string values are translated; keys, numbers, booleans, and `null` are not touched. Nested objects and arrays are traversed at every depth.
* Files must be strict, parseable JSON — no trailing commas, no comments. JSONC-style extensions are not supported.
* **Upload limit is 1 MB** regardless of plan. Large metadata payloads (e.g., DataCite, Zenodo, Backstage catalog dumps) may need to be split, or translated string-by-string via the text-translation API.
* Embedded HTML or Markdown inside string values (common in Contentful Rich Text and similar CMS payloads) is handled — DeepL translates the natural-language text and attempts to preserve the embedded markup. Review the output for complex rich-text content.
* To protect specific values from translation, encode them as non-strings (numbers/booleans/null) or pre-process the file to strip them.
**IDML**
* InDesign embeds font references, not the fonts themselves. If the target language uses characters not in the original font (e.g., Japanese in a Latin-only font), the output may show boxes or substituted glyphs — open the translated file in InDesign and swap fonts before distributing.
* Translated text is often longer than the source. Expect overset text (indicated by a red "+" in InDesign) in fixed-size frames; review and resize after translation.
* IDML does not use `translate="no"`. Protect content via InDesign character styles or by removing the affected text frames before exporting.
**MIF**
* The file must be genuine Adobe FrameMaker MIF. `.mif` files from other tools (e.g. Quartus memory-init files, MathML wrapped as MIF) currently return HTTP 500 rather than being rejected cleanly. Rename or convert them before uploading.
* MIF 8.00 and later are supported. Older MIF variants are best-effort — open and re-save from a recent FrameMaker version if the upload fails.
* Save as UTF-8 from FrameMaker before uploading; the file should begin with a `` header.
* Some valid FrameMaker 10 MIF files may fail with HTTP 500 — try re-saving from a newer FrameMaker version.
* MIF does not use `translate="no"`. Protect content via FrameMaker conditional text or character formatting.
### Polling and translation time
Translation time depends on document size and server load: small documents typically finish in seconds, larger ones in 1-2 minutes once translation has started. Poll the [status endpoint](/api-reference/document/check-document-status) at regular intervals or with exponential backoff. Treat the `seconds_remaining` field as a rough estimate only; it can be unreliable and occasionally returns implausible values (e.g. 2^27).
### Using glossaries with documents
You can apply a glossary to a document translation with the `glossary_id` parameter (or up to 5 glossaries with `glossary_ids`). This requires the `source_lang` parameter to be set, and the glossary's language pair has to match the language pair of the request.
### Document format conversions
By default, the translated document comes back in the same format as the input. Two conversions differ:
* Translating a `.doc` file returns a `.docx` file.
* With the `output_format` parameter on upload, you can translate a PDF and receive an editable Microsoft Word document (`output_format=docx`). No other input formats support alternative output formats.
### Error 429: Too Many Requests
This error may occur when:
* You send concurrent document translation requests that exceed your account quota.
* You have too many un-retrieved or non-downloaded translated documents.
Documents are stored for only a brief period and must be downloaded promptly after translation.
To avoid this error, we recommend implementing the following measures:
* Polling document translation status, taking into account its frequency in order to prevent excessive load
* Quicker time to document retrieval
* Retries with exponential backoff
### Error 456: Quota Exceeded
This error indicates that your latest document translation request has exceeded the included characters (API Free) or the character limit you have set (API Pro) for your account.
Further document translation requests will not be processed.
Check out our [code example](https://github.com/DeepL/deepl-node/tree/main/examples/bulk-translation) on how to implement bulk document translations.
# Error handling
Source: https://developers.deepl.com/docs/best-practices/error-handling
Errors are indicated by [standard HTTP status codes](https://developer.mozilla.org/docs/Web/HTTP/Status). It is important to make sure that your application handles errors in an appropriate way. To that end, please consult the list of expected status code results that are provided with each endpoint's documentation in the API Reference.
* **HTTP 429: too many requests.** This is an error that you might receive when sending many API requests in a short period of time. Your application should be configured to resend the requests after some delay. Specifically, we recommend implementing retries with exponential backoff. This is implemented in all of the official, DeepL-supported [client libraries](/docs/getting-started/client-libraries).
* **HTTP 456: quota exceeded** **If you're a Free API user**, you'll receive this error when the monthly 500,000 character limit of your subscription has been reached. You can consider [upgrading your subscription](https://www.deepl.com/pro) if you need more character volume. **If you're a Pro API user**, you'll receive this error when your [Cost Control](/docs/best-practices/cost-control) limit has been reached, and you can increase or remove your Cost Control limit if you need to continue translating. You can also use the [usage endpoint](/api-reference/usage-and-quota/check-usage-and-limits) to find out your currently used and available quota.
* **HTTP 500: internal server error** This is an error you'll receive if there are temporary errors in DeepL Services. Your application should be configured to resend the requests after some delay. Specifically, we recommend implementing retries with exponential backoff. This is implemented in all of the official, DeepL-supported [client libraries](/docs/getting-started/client-libraries). You can check the [API Status Page](https://status.deepl.com/?tab=api) for current service availability and incident information.
The service dynamically adjusts to the load on the system. Please wait to stop receiving errors to send more requests again. As the service adapts, you will be able to send increasingly more requests within a given amount of time without encountering errors.
Additional information may be provided by a JSON response that contains more details about the error. In this case, this additional information will be contained in the message key.
# Estimating Character Usage
Source: https://developers.deepl.com/docs/best-practices/estimating-character-usage
Learn how to estimate your DeepL API character usage to find the right plan.
DeepL bills by the number of characters in your **source text**, measured in [Unicode code points](/docs/resources/usage-limits#your-usage). "A", "Δ", "あ", and "深" each count as one character. This means you can estimate your usage entirely on your own, without calling the API. All you need is access to your content.
This guide walks through techniques for counting characters in different content types, projecting monthly usage, and validating your estimates.
## Before you start
DeepL bills by source-text length in Unicode code points. Characters in the [`context` parameter](/docs/learning-how-tos/examples-and-guides/how-to-use-context-parameter) and HTML/XML tags (when [tag handling](/docs/translate/translating-xml) is enabled) do not count. For the full billing rules and per-document character minimums, see [Usage and limits](/docs/resources/usage-limits#your-usage).
## Estimate website content
You don't need to crawl your entire site. Pick 5-10 representative pages (a mix of short and long ones), count the characters on each, and use the average to extrapolate across your total page count.
The script below strips HTML from a page and returns the character count. Run it against a handful of URLs to get your average.
```python theme={null}
from html.parser import HTMLParser
from urllib.request import urlopen
class TextExtractor(HTMLParser):
def __init__(self):
super().__init__()
self.parts = []
self._skip = False
def handle_starttag(self, tag, attrs):
self._skip = tag in ("script", "style", "noscript")
def handle_endtag(self, tag):
self._skip = False
def handle_data(self, data):
if not self._skip:
self.parts.append(data)
def get_text(self):
return " ".join(self.parts)
def count_characters(url):
html = urlopen(url).read().decode("utf-8", errors="replace")
extractor = TextExtractor()
extractor.feed(html)
return len(extractor.get_text())
sample_urls = [
"https://example.com/about",
"https://example.com/products",
"https://example.com/faq",
"https://example.com/blog/recent-post",
"https://example.com/contact",
]
total = 0
for url in sample_urls:
chars = count_characters(url)
total += chars
print(f"{chars:>10,} {url}")
avg = total // len(sample_urls)
print(f"\n{'Average':>10} {avg:,} characters per page")
```
Then multiply the average by your total number of pages:
```
Estimated total = average characters per page × total pages on site
```
If you want an exact count instead of a sample-based estimate, you can extend the script to crawl your full sitemap or use a crawler like [Scrapy](https://scrapy.org/) to discover all pages automatically.
## Estimate from a CMS or database
If your content lives in a CMS or a database, query it directly. This is often the most accurate approach because it reflects exactly what you'll send to the API.
Most CMSes (Drupal, WordPress, Contentful, etc.) do not provide a built-in way to see total word or character counts across all published content. You'll typically need to query the underlying database or use the CMS export/API to pull content and count locally.
```sql theme={null}
-- Example: estimate total characters across a content table
SELECT
SUM(CHAR_LENGTH(body)) AS total_characters,
COUNT(*) AS total_entries
FROM content
WHERE status = 'published';
```
For a Drupal site specifically, the `node_field_data` and `node__body` tables contain page titles and body content. For WordPress, query the `wp_posts` table filtering on `post_status = 'publish'`.
## Estimate document content
For documents you plan to translate via the [Document Translation API](/api-reference/document/upload-and-translate-a-document), you can extract text locally to get a rough character count.
```python theme={null}
import zipfile
import xml.etree.ElementTree as ET
import os
def count_docx_characters(path):
with zipfile.ZipFile(path) as z:
xml_content = z.read("word/document.xml")
root = ET.fromstring(xml_content)
ns = {"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"}
texts = root.findall(".//w:t", ns)
return sum(len(t.text) for t in texts if t.text)
for path in ["report.docx", "presentation.pptx"]:
if os.path.exists(path):
chars = count_docx_characters(path)
print(f"{path}: {chars:,} characters")
```
For PDFs, use a library like PyMuPDF or pdfplumber to extract text.
Keep in mind that your local count is an approximation. DeepL's document processing pipeline may extract text differently than a local script, for example, by reading text embedded in images or charts via OCR. Treat your local count as a lower bound.
[Per-document character minimums](/docs/resources/usage-limits#maximum-upload-limits-per-document-format) apply for certain file formats, so the billed count may be higher than the actual text content.
## Project monthly usage
Once you know your total source characters, multiply by the number of target languages and your expected update frequency.
```
Monthly usage = source characters × target languages × update factor
```
For example, a website with 500,000 source characters translated into 5 languages with \~10% of pages updated monthly:
```
Initial translation: 500,000 × 5 = 2,500,000 characters
Monthly updates: 50,000 × 5 = 250,000 characters/month
```
If you're translating the same content into multiple languages, each language counts separately toward your character usage.
## Validate your estimate
Once you've estimated your characters, you can [sign up for a DeepL API plan for free](https://www.deepl.com/en/pro#api) and test a small sample against the live API. Use the [`show_billed_characters`](/api-reference/translate/request-translation) parameter to compare actual billed characters against your local count. DeepL intends to include `billed_characters` in responses by default in the future, with advance notice to API users before the change.
```bash theme={null}
curl -X POST https://api.deepl.com/v2/translate \
--header 'Authorization: DeepL-Auth-Key YOUR_AUTH_KEY' \
--header 'Content-Type: application/json' \
--data '{
"text": ["Sample text from your website or document."],
"target_lang": "DE",
"show_billed_characters": true
}'
```
```json theme={null}
{
"translations": [
{
"detected_source_language": "EN",
"text": "Beispieltext von Ihrer Website oder Ihrem Dokument."
}
],
"billed_characters": 46
}
```
Run this on 5-10 representative pages or documents and compare the `billed_characters` value against your local character count. They should be very close. Differences typically come from HTML tags or whitespace that your local extraction handles differently than the API.
## Monitor actual usage
After you start translating, monitor actual usage against your estimates:
* **[`/v2/usage` endpoint](/api-reference/usage-and-quota/check-usage-and-limits)** - programmatic access to your current billing period consumption
* **[Usage Analytics Dashboard](/docs/learning-how-tos/cookbook/usage-analytics-dashboard)** - visualize usage across API keys with the open-source demo dashboard
* **[API Usage Logger](/docs/learning-how-tos/cookbook/api-usage-logger)** - per-request logging with billed characters, language pairs, and reporting tags
* **[Cost Control](/docs/best-practices/cost-control)** - set a monthly character limit on your Pro API subscription to cap spend
# Language detection
Source: https://developers.deepl.com/docs/best-practices/language-detection
Our translation and document translation endpoints allow you to automatically detect the source language or set it. It is recommended to set the source language whenever possible, as this has a positive effect on translation quality. If you cannot specify the source language, the more context you provide, the better your results will be. Single words can lead to incorrect language detection, so the longer your text, the more reliable it will be.
If you are translating single words or very short sentences, the results will generally be more reliable if the source language is specified. You can find all available [source languages here](/api-reference/languages/retrieve-supported-languages), and you can read more about translation context in the next chapter.
# Pre-production checklist
Source: https://developers.deepl.com/docs/best-practices/pre-production-checklist
Here's what we recommend reviewing to get your DeepL API application ready for production.
As you prepare to open your DeepL-powered application up to the world, these tips will help you get ready for production.
1. **Error handling:** If you receive 429 or 500 errors, use retries with exponential backoff. If you use one of [DeepL's official client libraries](/docs/getting-started/client-libraries), you get this functionality out-of-the-box.
2. **Persistent HTTP connection:** Especially for use cases with low latency requirements, we recommend using a persistent HTTP connection. [DeepL's official client libraries](/docs/getting-started/client-libraries) use [Keep-Alive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive) by default.
3. **Use the correct content type:** For text translation (the `/translate` endpoint), use form-encoded (`Content-Type: application/x-www-form-urlencoded`) or JSON-encoded (`Content-Type: application/json`) request bodies.
* Uploading a file for document translation requires an HTTP POST request with `Content-Type: multipart/form-data`; this content type should not be used for text translation.
4. **No query parameters:** Please do not make API requests using query parameters. The examples throughout the API reference include properly formed HTTP POST requests. For security reasons, be especially sure not to send your authentication key via query parameters.
5. **CORS requests:** It's not possible to send requests to the DeepL API from the browser, as requests to third-party APIs from front-end applications would expose your credentials on the web. [You can learn more here](/docs/best-practices/cors-requests).
6. **Translation context:** DeepL considers the broader context of a source text or document when translating. In general, including more context in a source text or document can result in a higher-quality DeepL translation. For text translation, you can also try the [`context` parameter](/api-reference/translate/request-translation). [Learn more about working with context here](/docs/learning-how-tos/examples-and-guides/how-to-use-context-parameter).
7. **Cache results from translation requests:** Storing API responses lets apps serve content faster and avoids extra costs from repeated requests for unchanged content.
# Custom instructions
Source: https://developers.deepl.com/docs/customize/custom-instructions
Learn how to create effective custom instructions to customize your translation behavior
The `custom_instructions` parameter allows you to provide natural language instructions that customize how the DeepL API translates text. This guide provides best practices for creating effective custom instructions that produce consistent, high-quality results.
## What are custom instructions?
Custom instructions enable you to guide the translation engine with specific requirements for your use case. You can use custom instructions to:
* Control tone and style (e.g., "Use a friendly, diplomatic tone")
* Specify domain-specific terminology preferences
* Define formatting conventions
* Adjust formality levels beyond the standard `formality` parameter
* Provide context-specific guidance for specialized content
Custom instructions can also be used in conjunction with other features, such as glossaries, style rules, and the `context` parameter.
## How instructions are applied
Custom instructions are applied while the text is translated, sentence by sentence and passage by passage. They are not applied as an editing pass over the finished document. This makes them effective for local changes, such as word choice, tone, and the structure of individual sentences, and unreliable for changes that depend on seeing the whole text at once.
An instruction that describes the shape of the entire text, such as reordering an argument, converting lists into running text, or adding a summary, is applied to each passage individually. Instead of restructuring the document once, it restructures every passage, which typically lowers translation quality.
Write instructions that apply to the structure of one or more sentences, not to the structure of the entire text.
❌ **INCORRECT: Whole-text restructuring** — "Structure the text as a continuous chain: observation, problem framing, definition, implications"
✅ **CORRECT: Sentence-level structure** — "Split sentences longer than 25 words into two shorter sentences"
If you need the translated document restructured, translate it with local instructions only, then restructure the result in a separate step.
## Best practices
### 1. Write rules in English or the target language
Custom instructions should be written in either English or the target language. This ensures the translation engine can properly interpret and apply your instructions.
**English instruction** (works for all target languages) — "Use informal language appropriate for a mobile app"
**Target language instruction** (French) — "Utiliser un langage informel adapté à une application mobile"
### 2. Formulate rules positively
State what the translation should do, rather than what it should not do. Positive formulations are clearer and more effective.
❌ **INCORRECT: Negative formulation** — "Don't use overly formal language or avoid casual expressions"
✅ **CORRECT: Positive formulation** — "Use a conversational, friendly tone"
### 3. One instruction per rule
Each custom instruction should contain a single, focused directive. This makes your instructions clearer and more predictable.
❌ **INCORRECT: Multiple instructions combined** — "Use technical terminology, maintain formal tone, and convert measurements to metric"
✅ **CORRECT: Separate instructions**
* "Use technical terminology appropriate for engineers"
* "Maintain a formal, professional tone"
* "Convert imperial measurements to metric units"
### 4. Provide details on when and how to apply the rule
Include context about when the rule should be applied and specific guidance on how to apply it.
❌ **INCORRECT: Vague instruction** — "Handle gender appropriately"
✅ **CORRECT: Specific instruction with context** — "When translating role titles, use gender-neutral forms where available in the target language"
**Elements of a detailed instruction:**
* **When**: Conditions that trigger the rule
* **What**: Specific elements to modify
* **How**: Desired behavior or format
### 5. Avoid too many conditions per rule
Keep conditions simple and focused. Complex conditional logic reduces effectiveness.
❌ **INCORRECT: Too many conditions** — "If the text contains product names or brand terms, unless they appear in headings or quotes, and the context is marketing material rather than technical documentation, then capitalize all words except articles"
✅ **CORRECT: Simplified conditions**
* "Capitalize product names and brand terms in marketing content"
* "Preserve original capitalization in technical documentation"
### 6. Write instructions as translation directives, not chatbot prompts
Custom instructions guide translation behavior. They should be concise directives, not conversational prompts.
❌ **INCORRECT: Chatbot-style prompt** — "Please translate this text in a way that would be appropriate for a luxury fashion brand. Make sure to sound elegant and sophisticated, like you're speaking to a high-end clientele."
✅ **CORRECT: Clear translation directive** — "Use elegant, sophisticated language appropriate for luxury fashion"
## Combining multiple instructions
All active instructions apply to every translation together; there is no priority order among them. However, the broader and more transformative an instruction is, the more strongly it shapes the output. A single instruction that rewrites heavily can drown out the effect of your more specific instructions.
To keep a set of instructions predictable:
* Add instructions one at a time, and test each against representative source texts before adding the next
* Keep each instruction local and specific, following the best practices above
* If translation quality drops after a change, remove the broadest instruction first and retest
## Technical constraints
When using custom instructions, keep these constraints in mind:
* **Maximum instructions**: Up to 10 custom instructions per request
* **Character limit**: Each instruction can contain a maximum of 300 characters
* **Supported target languages**: `de`, `en`, `es`, `fr`, `it`, `ja`, `ko`, `zh`, or any variants of these languages
* **Model compatibility**: Custom instructions are compatible with all `model_type` values.
## Related documentation
* [Text translation API reference](/api-reference/translate/request-translation)
* [Working with context](/docs/learning-how-tos/examples-and-guides/how-to-use-context-parameter)
* [Style rules API](/docs/customize/using-style-rules)
* [Multilingual glossaries](/docs/customize/managing-glossaries)
# How to Apply Customizations to Language Variants
Source: https://developers.deepl.com/docs/customize/customizations-for-variants
Learn how to apply glossaries and style rules when translating into language variants like FR-CA or EN-GB.
DeepL supports regional language variants such as `PT-BR` vs `PT-PT`, or `FR-CA` vs `FR-FR`. When applying customizations like glossaries and style rules, customizations must be created using the root language code, but can be applied to any variant of that language.
**This guide shows you:**
* How to create customizations using root language codes (e.g. `PT`, `FR`)
* How to pass a customization when translating into a variant target (e.g. `pt-BR`, `fr-CA`)
* A practical example of using variant-specific glossaries to enforce locale-appropriate terminology
## Creating a customization for a language
DeepL distinguishes between root language codes (e.g. `pt`) and language variant codes (e.g. `pt-BR`). Customizations like glossaries and style rules must be created with the root code. Attempting to create one with a variant code will fail.
| **Use this** | **Not this** |
| :----------- | :------------------- |
| `zh` | `zh-Hant`, `zh-Hans` |
| `pt` | `pt-BR`, `pt-PT` |
| `fr` | `fr-CA`, `fr-CH` |
| `de` | `de-CH` |
| `it` | `it-CH` |
| `es` | `es-ES`, `es-419` |
See [supported languages](/docs/getting-started/supported-languages) for the full list.
The following example creates two glossaries to enforce different terms for "invoice" in Brazilian and European Portuguese. Both use the root language code `PT`:
```python Example: Create glossaries with root language codes theme={null}
import deepl
translator = deepl.Translator("YOUR_AUTH_KEY")
glossary_br = translator.create_glossary(
"PT-BR Invoice Glossary",
source_lang="EN",
target_lang="PT", # root code — not PT-BR
entries={"invoice": "nota fiscal"}
)
glossary_pt = translator.create_glossary(
"PT-PT Invoice Glossary",
source_lang="EN",
target_lang="PT", # root code — not PT-PT
entries={"invoice": "fatura"}
)
```
## Applying a customization to a language variant
Once a customization is created with a root language code, pass its ID in the `/translate` call with a variant `target_lang`:
```python Example: Apply glossaries to variant targets theme={null}
result_br = translator.translate_text(
"Your invoice is ready to view.",
source_lang="EN",
target_lang="PT-BR",
glossary=glossary_br.glossary_id
)
result_pt = translator.translate_text(
"Your invoice is ready to view.",
source_lang="EN",
target_lang="PT-PT",
glossary=glossary_pt.glossary_id
)
print(result_br.text) # Sua nota fiscal está pronta para ser visualizada.
print(result_pt.text) # A sua fatura está pronta a ser visualizada.
```
## CAT tools
Some CAT tools may prevent applying a glossary or style rule linked to a root code language when the target language is a variant. This is an incorrect limitation that does not reflect the DeepL API's behavior. You'll need to reach out to your CAT tool provider to request this restriction is removed.
***
## Next steps
* **Apply glossaries in practice:** See [Glossaries in the real world](/docs/customize/glossaries-in-the-real-world) for a full worked example
* **Translate between variants:** Learn about the Write API and style rules in [How to translate between language variants](/docs/learning-how-tos/examples-and-guides/translating-between-variants)
* **Manage style rules:** Explore the [style rules API reference](/docs/customize/using-style-rules) to create and retrieve style rules
* **Improve translation quality:** See how [the context parameter](/docs/learning-how-tos/examples-and-guides/how-to-use-context-parameter) can further refine your translations
# Glossaries in the Real World
Source: https://developers.deepl.com/docs/customize/glossaries-in-the-real-world
A quick guide to using DeepL glossaries in translations
### What are glossaries and why should you care?
A great feature of DeepL API is the ability to improve consistency when translating content across multiple languages. As an API user you can do this by including glossaries in your translations, which allow you to fine-tune how words and phrases should be translated from one language to another.
This is perfect for situations where technical terms or product information needs to be standardized - saving time and money by reducing the need for manual editing in a translation workflows.
### A simple example from The Acme Startup Company
The best way to show how something works is to use a real world use case, and for this we’re heading to the cool headquarters of (the completely fictional) Acme Startup Company.
Acme have customers from all over the world and offer support over email and their in-app chat system. Their support staff work around the globe in order to provide 24 hour support, but different time zones and language difference might mean customers get a different experience each time they speak to an agent. Saying “***Good morning***” to a customer when it’s 7pm for them doesn’t sound great.
Acme prides itself on the quality of its support and wants to standardize how support agents interact with customers without removing human interaction. Acme reviewed the chat and email transcripts of a cross section of customer service messages, and built an initial list of the words and phrases they wanted to standardize when a customer is greeted.
Let’s dive in and help Acme out!
### Getting started
First, we’re going to need a DeepL account in order to get hold of an API key so we can authenticate our API requests. For this demo, we’re going to use the DeepL Python client library in order to make the API requests, but all the glossaries functionality is available using cURL or any of our other client libraries. Check out the API docs and DeepL GitHub for more information.
First, we’ll install the DeepL Python library from the command line:
`pip install --upgrade deepl`
From here we can start putting together our Python code. In a file called **main.py**, We’ll begin by constructing a translator object, including your own auth key you can find in your account:
```python main.py theme={null}
import deepl
auth_key = "19f172ae-03a9-..."
translator = deepl.Translator(auth_key)
```
And finally, we’ll send a simple translation request, with an example of what Acme are currently experiencing:
```python main.py theme={null}
import deepl
auth_key = "19f172ae-03a9-..."
translator = deepl.Translator(auth_key)
result = translator.translate_text("Good evening, Gabrielle", target_lang="DE")
print(result.text) # "Guten Abend, Gabrielle"
```
As you can see, if Gabrielle was receiving this in the morning of their time zone, it might not set the right tone Acme are looking for in their customer service quality. Let’s help Acme out by creating a simple solution using a Glossary.
### Creating a Glossary
To create our glossary, we’ll create a new file called **`glossary.py`** - this is because we only need to create the glossary once. Start with the same code to construct a translator object:
```python glossary.py theme={null}
import deepl
auth_key = "19f172ae-03a9-..."
translator = deepl.Translator(auth_key)
```
Then, we can create a list of entries we want to recognize and change when translating from English to German:
```python glossary.py theme={null}
import deepl
auth_key = "19f172ae-03a9-..."
translator = deepl.Translator(auth_key)
entries = {"Good morning": "Hallo", # "Guten Morgen"
"Good afternoon": "Hallo", # "Guten Tag"
"Good evening" : "Hallo", # "Guten Abend"
"Greetings": "Hallo", # "Grüße"
"Dear": "Hallo"} # "Liebe"
```
Lastly, we create the glossary by calling `translator.create_glossary`, giving it a name, setting the source and target languages, and assigning the entries we just created:
```python glossary.py theme={null}
import deepl
auth_key = "19f172ae-03a9-..."
translator = deepl.Translator(auth_key)
entries = {"Good morning": "Hallo", # "Guten Morgen"
"Good afternoon": "Hallo", # "Guten Tag"
"Good evening" : "Hallo", # "Guten Abend"
"Greetings": "Hallo", # "Grüße"
"Dear": "Hallo"} # "Liebe"
acme_glossary = translator.create_glossary(
"Greetings Glossary",
source_lang="EN",
target_lang="DE",
entries=entries,
)
print(acme_glossary.glossary_id) # "def3a26b-3e84..."
```
Running `glossary.py` will give us a response containing `glossary_id`, which we can add to our initial translation in `main.py`. Also be aware that when using a glossary, you will also need to include `source_lang` as part of your request:
```python main.py theme={null}
import deepl
auth_key = "19f172ae-03a9-..."
translator = deepl.Translator(auth_key)
result = translator.translate_text("Good evening, Gabrielle", source_lang="EN", target_lang="DE", glossary="def3a26b-3e84...")
print(result.text) # "Hallo, Gabrielle"
```
You’ll now see that the greeting of “***Guten Abend, Gabrielle***” has now been replaced with the much more consistent “***Hallo, Gabrielle***”. Try changing “***Good evening***” to “***Good morning***”, and you’ll see the Glossary doing its job and keeping all customer service messaging in harmony.
### Wrapping up
This has been a really simple - but powerful - example to highlight how glossaries can help organizations maintain quality, accuracy and consistency in their messaging across multiple languages. Although this was a small snapshot into how glossaries could be used to make greetings consistent, it’s clear just how powerful glossaries can be when expanded into covering content such as product catalogs or technical documentation, where consistent naming is crucial.
Glossaries are incredibly flexible and give you the power to fine-tune your translations. Some more examples for you to try out could be:
* Always changing “***Vereinigtes Königreich***” to “***UK***” (rather than “United Kingdom”)
* Updating “***pharmacy***” in locales where it is more common to say “***chemist***” or “***drug store***”
* Ensuring “***Casques d'écoute***” always translates to “***headphones***” in a product catalog (instead of potentially “headset”)
# Glossary v2 vs v3 Endpoints
Source: https://developers.deepl.com/docs/customize/glossary-v2-vs-v3-endpoints
How the v3 glossary endpoints differ from the deprecated v2 endpoints, what to watch out for when mixing them, and how to work with v2's immutability.
DeepL's API has two generations of glossary endpoints. The [v2 endpoints](/api-reference/glossaries/create-a-glossary) create, delete, and retrieve **monolingual** glossaries: glossaries that map one language to another. The [v3 endpoints](/api-reference/multilingual-glossaries/create-a-glossary) include all v2 functionality, plus:
* v3 lets you **edit** glossaries
* v3 supports **multilingual** glossaries: a collection of dictionaries covering multiple language pairs
We recommend using v3 for all glossary work; v2 is kept for backward compatibility. There is no need to migrate your existing glossaries: you can use v3 endpoints with any glossary, whichever version created it. Glossaries from either version work in all translation endpoints, both [`/translate`](/api-reference/translate/request-translation) and [`/document`](/api-reference/document/upload-and-translate-a-document).
## Differences between v2 and v3
A v2 glossary is a single list of mappings with one source and one target language:
```json theme={null}
{
"glossary_id": "def3a26b-3e84-45b3-84ae-0c0aaf3525f7",
"name": "My Glossary",
"source_lang": "en",
"target_lang": "de",
"entries": {
"Hello": "Hallo"
},
"creation_time": "2025-08-03T14:16:18.329Z"
}
```
A v3 glossary holds a collection of **dictionaries**, each with its own language pair. The same glossary can carry the reverse mapping too:
```json theme={null}
{
"glossary_id": "def3a26b-3e84-45b3-84ae-0c0aaf3525f7",
"name": "My Glossary",
"dictionaries": [
{
"source_lang": "en",
"target_lang": "de",
"entries": {
"Hello": "Hallo"
}
},
{
"source_lang": "de",
"target_lang": "en",
"entries": {
"Hallo": "Hello"
}
}
],
"creation_time": "2025-08-03T14:16:18.329Z"
}
```
The v3 endpoints handle glossary management only; use v2 for everything else, including translation itself. The deprecated `/v2/glossary-language-pairs` endpoint is also superseded: use [`GET /v3/languages?resource=glossary`](/docs/languages/using-the-languages-api) instead.
## Can I keep using v2?
You can, but we recommend switching: new features land on v3, and the v2 glossary endpoints may be deprecated or removed at some point. If an immediate switch isn't possible, keep these implications in mind:
* Once a glossary has been edited via v3, v2 endpoints can no longer query it correctly (for example, the "get entries" call). To avoid data loss, deleting such a glossary through v2 is disabled; use the v3 deletion endpoint.
* Avoid mixing versions across a team or codebase. Glossaries created via v3 are still queryable via v2, where they can't be displayed correctly, which causes confusion.
Don't use both v2 and v3 glossary endpoints in the same integration. Editing a glossary via v3 changes how it behaves on v2 endpoints.
## Editing under v2: the workaround
v2 glossaries are immutable: once created, the entries for a given glossary ID cannot be modified. If you stay on v2, identify glossaries by **name** instead of ID in your application, and modify them with this procedure:
1. [Retrieve](/api-reference/glossaries/retrieve-glossary-entries) and store the current glossary's entries
2. Modify the entries locally
3. [Delete](/api-reference/glossaries/delete-a-glossary) the existing glossary
4. [Create a new glossary](/api-reference/glossaries/create-a-glossary) with the same name
On v3, none of this is necessary; edit dictionaries directly as shown in [Managing Glossaries](/docs/customize/managing-glossaries).
## Client libraries
Each of our [client libraries](/docs/getting-started/client-libraries) provides a guide explaining how to migrate its glossary support to v3.
# Improving Transcription with Spoken Terms
Source: https://developers.deepl.com/docs/customize/improving-transcription-with-spoken-terms
Keep company names, acronyms, and product terminology transcribed correctly in Voice API sessions by creating and maintaining a Spoken Terms collection.
In Voice API sessions, Spoken Terms ensure that the vocabulary that matters to you, such as company names, acronyms, product names, and people's names, is transcribed with the exact spelling you choose. You provide the words, and speech recognition uses your spelling whenever they're spoken. Unlike [glossaries](/docs/customize/managing-glossaries), Spoken Terms are monolingual. They affect how speech is recognized, not how the recognized text is translated.
This guide shows you how to set up Spoken Terms via the API and keep them current as your vocabulary grows. You can also manage them in [DeepL Home](https://www.deepl.com/en/voice/spoken-terms).
Spoken Terms are available on all plans that include the DeepL Voice API. The number of collections you can create depends on your [plan](https://www.deepl.com/en/pro#api).
## Collect domain-specific terms
Review your calls for domain- and company-specific words you want transcribed with complete accuracy. Collect them in a **term list**: the terms, written exactly as you want them to appear in your transcripts (terms are case-sensitive). In the next step, you'll send this list to the DeepL API.
```text theme={null}
DeepL
API
webhook
```
Each term list holds at most 300 characters in total (see [Spoken Terms Requirements](/docs/customize/spoken-terms-requirements) for all size and content rules), so spend the budget on the terms that matter most rather than your full vocabulary.
## Create a collection
Term lists are stored in a **Spoken Terms collection**, which can hold one term list for each language. Create a collection with [`POST /v3/spoken-terms`](/api-reference/spoken-terms/create-spoken-terms-collection):
```sh Example request theme={null}
curl -X POST https://api.deepl.com/v3/spoken-terms \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"name": "Technical Terms",
"term_lists": [
{
"lang": "en",
"entries": "DeepL\nAPI\nwebhook"
}
]
}'
```
```json Example response theme={null}
{
"spoken_terms_id": "def3a26b-3e84-45b3-84ae-0c0aaf3525f7",
"name": "Technical Terms",
"term_lists": [
{
"lang": "en",
"entry_count": 3
}
],
"creation_time": "2025-08-03T14:16:18.329Z"
}
```
Store the `spoken_terms_id`; every voice session that should use these terms passes it.
## Start a voice session with your terms
Include the `spoken_terms_id` when you [create a voice session](/api-reference/voice/request-session). The terms for the session's source language are then recognized correctly in the transcription:
```sh Example request theme={null}
curl -X POST https://api.deepl.com/v3/voice/realtime \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"source_media_content_type": "audio/pcm; encoding=s16le; rate=16000",
"source_language": "en",
"target_languages": ["de"],
"spoken_terms_id": "def3a26b-3e84-45b3-84ae-0c0aaf3525f7"
}'
```
See the [Real-Time Voice Quickstart](/docs/voice/real-time-voice-quickstart) for an example of the full Voice API session flow.
To also control how your terms are **translated**, pass a `glossary_id` in the same session. Spoken Terms control how speech is recognized, and glossaries control how it's translated.
Spoken Terms are available for all [DeepL Voice languages](https://support.deepl.com/hc/en-us/articles/26625846174364-DeepL-Voice-languages). To check a language programmatically, call [`GET /v3/languages?resource=voice`](/docs/languages/using-the-languages-api) and look for the `spoken_terms` feature key on the source language.
## Add terms as your vocabulary grows
To add terms without resending the existing ones, [`PATCH` the collection](/api-reference/spoken-terms/edit-spoken-terms-details); entries you pass for a language are merged into its existing term list:
```sh Example request theme={null}
curl -X PATCH https://api.deepl.com/v3/spoken-terms/def3a26b-3e84-45b3-84ae-0c0aaf3525f7 \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"term_lists": [
{
"lang": "en",
"entries": "authentication\nendpoint"
}
]
}'
```
```json Example response theme={null}
{
"spoken_terms_id": "def3a26b-3e84-45b3-84ae-0c0aaf3525f7",
"name": "Technical Terms",
"term_lists": [
{
"lang": "en",
"entry_count": 5
}
],
"creation_time": "2025-08-03T14:16:18.329Z"
}
```
Running sessions aren't affected; the updated terms apply to sessions started after the change.
## Replace a language's terms
To rebuild a language's term list from scratch, for example from an updated product catalog, [`PUT` the term list](/api-reference/spoken-terms/replace-or-create-term-list) with the new entries. Unlike `PATCH`, which adds to the existing list, `PUT` replaces the list entirely (or creates it, if the language is new to the collection):
```sh Example request theme={null}
curl -X PUT https://api.deepl.com/v3/spoken-terms/def3a26b-3e84-45b3-84ae-0c0aaf3525f7/term-lists \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"lang": "en",
"entries": "DeepL\nAPI\nwebhook\nintegration"
}'
```
```json Example response theme={null}
{
"lang": "en",
"entry_count": 4
}
```
See the [Spoken Terms reference](/api-reference/spoken-terms/create-spoken-terms-collection) for full details on managing collections. Size and content rules are collected in [Spoken Terms Requirements](/docs/customize/spoken-terms-requirements).
# Managing Glossaries
Source: https://developers.deepl.com/docs/customize/managing-glossaries
Create, edit, retrieve, and delete DeepL glossaries with the v3 endpoints, and apply them in translation requests.
Glossaries let you specify exact translations for words and short phrases. During translation, DeepL intelligently flexes entries to account for case, gender, tense, and other grammar features when the target language has flexion. This guide shows how to manage glossaries programmatically with the [v3 glossary endpoints](/api-reference/multilingual-glossaries/create-a-glossary) and apply them in translations.
A **glossary** contains one or more **dictionaries**. A dictionary maps source phrases to target phrases for a single language pair, in one direction:
| **French →** | **Spanish** |
| :----------- | :---------- |
| belle | hermosa |
| delicieux | exquisito |
To apply the same terminology in both directions, add a second dictionary with the reverse mapping (Spanish → French). You can create dictionaries for [any language that supports glossaries](/docs/getting-started/supported-languages); to check programmatically, call [`GET /v3/languages?resource=glossary`](/docs/languages/using-the-languages-api).
If you're new to glossaries, start with [Glossaries in the Real World](/docs/customize/glossaries-in-the-real-world), a hands-on tutorial that builds one from scratch.
## Entry formats
Glossary entries are formatted as CSV (comma-separated values) or TSV (tab-separated values), one entry per line, source phrase first:
```csv CSV entries theme={null}
hermosa,belle
exquisito,delicieux
```
You can also enclose each phrase in quotation marks. CSV entries follow standard CSV conventions:
* Fields containing double quotes or commas must be enclosed in double quotes
* A double quote inside a quoted field is escaped by doubling it (`""`)
TSV is identical except that a tab separates the source and target phrases. In CSV, you can optionally append the source and target language after the phrases; entries whose languages don't match the dictionary's language pair are ignored.
## Create a glossary
Send `POST /v3/glossaries` an array of one or more dictionaries. This example creates a glossary with an English → German dictionary and its reverse:
```sh Example request theme={null}
curl -X POST https://api.deepl.com/v3/glossaries \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"name": "My Glossary",
"dictionaries": [
{
"source_lang": "en",
"target_lang": "de",
"entries": "Hello\tGuten Tag",
"entries_format": "tsv"
},
{
"source_lang": "de",
"target_lang": "en",
"entries": "Guten Tag\tHello",
"entries_format": "tsv"
}
]
}'
```
```json Example response theme={null}
{
"glossary_id": "def3a26b-3e84-45b3-84ae-0c0aaf3525f7",
"ready": true,
"name": "My Glossary",
"dictionaries": [
{ "source_lang": "en", "target_lang": "de", "entry_count": 1 },
{ "source_lang": "de", "target_lang": "en", "entry_count": 1 }
],
"creation_time": "2025-08-03T14:16:18.329Z"
}
```
To create a glossary from an existing CSV file on the command line, use [`jq`](https://jqlang.github.io/jq/) to embed the file contents in the request body:
```sh Create a glossary from a CSV file theme={null}
curl -X POST https://api.deepl.com/v3/glossaries \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--header "Content-Type: application/json" \
--data "$(jq -Rs '{
"name": "My Glossary",
"dictionaries": [
{
"source_lang": "en",
"target_lang": "de",
"entries": .,
"entries_format": "csv"
}
]
}' glossary.csv)"
```
## Use a glossary in a translation
Include the `glossary_id` in a [`/v2/translate`](/api-reference/translate/request-translation) or [`/v2/document`](/api-reference/document/upload-and-translate-a-document) request. You must also set `source_lang`: glossaries can't yet be used with automatic source language detection.
```sh Example request theme={null}
curl -X POST https://api.deepl.com/v2/translate \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"text": ["Hello"],
"source_lang": "EN",
"target_lang": "DE",
"glossary_id": "def3a26b-3e84-45b3-84ae-0c0aaf3525f7"
}'
```
```json Example response theme={null}
{
"translations": [
{
"detected_source_language": "EN",
"text": "Guten Tag"
}
]
}
```
Glossaries apply to root languages, not specific variants: a glossary with target language `EN` applies when translating into `EN-US` and `EN-GB` alike, and must be created with the root code. See [How to Apply Customizations to Language Variants](/docs/customize/customizations-for-variants).
The `v3` endpoints handle glossary management only; translation itself stays on the `v2` endpoints.
## Edit a glossary
Two methods change an existing glossary, with different semantics:
| Method | Scope | Behavior |
| :----------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------- | :------------------------------------------------------------------------------------------------------------- |
| [`PUT /v3/glossaries/{id}/dictionaries`](/api-reference/multilingual-glossaries/replaces-or-creates-a-dictionary-in-the-glossary-with-the-specified-entries) | One dictionary | Creates the dictionary for the given language pair, or **replaces** it entirely if it exists |
| [`PATCH /v3/glossaries/{id}`](/api-reference/multilingual-glossaries/edit-glossary-details) | Whole glossary | Updates metadata like the name; entries passed for a language pair are **merged** into the existing dictionary |
For example, this `PATCH` renames a glossary and adds one entry to its English → German dictionary, keeping existing entries:
```sh Example request theme={null}
curl -X PATCH https://api.deepl.com/v3/glossaries/def3a26b-3e84-45b3-84ae-0c0aaf3525f7 \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"name": "Gertrude the glossary",
"dictionaries": [{
"source_lang": "en",
"target_lang": "de",
"entries": "Goodbye\tTschüß",
"entries_format": "tsv"
}]
}'
```
A single `PUT` or `PATCH` can change one dictionary. To change the same source phrase across multiple language pairs, make one call per dictionary.
## Retrieve glossaries
* [`GET /v3/glossaries`](/api-reference/multilingual-glossaries/list-all-glossaries) lists all your glossaries with per-dictionary metadata (no entries)
* [`GET /v3/glossaries/{id}`](/api-reference/multilingual-glossaries/retrieve-glossary-details) returns one glossary's metadata
* [`GET /v3/glossaries/{id}/entries`](/api-reference/multilingual-glossaries/retrieve-glossary-entries) returns the entries of a single dictionary, selected via `source_lang` and `target_lang` query parameters. Entries are currently returned in TSV format only.
To retrieve the contents of an entire glossary, iterate over its dictionaries and fetch each one's entries.
## Delete glossaries
* [`DELETE /v3/glossaries/{id}`](/api-reference/multilingual-glossaries/delete-a-glossary) deletes the whole glossary
* [`DELETE /v3/glossaries/{id}/dictionaries?source_lang=...&target_lang=...`](/api-reference/multilingual-glossaries/deletes-the-dictionary-associated-with-the-given-language-pair-with-the-given-glossary-id) deletes a single dictionary
## Limits and restrictions
* Each dictionary can contain up to 10 MB of entries; a glossary with five dictionaries can hold up to 50 MB in total
* The glossary name, each source phrase, and each target phrase can contain up to 1024 UTF-8 bytes
* Duplicate source entries are not allowed, and neither source nor target may be empty
* Entries must not contain control characters (such as `\t` or `\n` inside a phrase), Unicode newlines, or leading/trailing whitespace
* The number of glossaries per account is [limited by your plan](https://www.deepl.com/en/pro-api)
# Customize
Source: https://developers.deepl.com/docs/customize/overview
Tailor DeepL translations to your domain with glossaries, style rules, custom instructions, and translation memories.
DeepL's customization features let you control terminology, style, and consistency across your translations.
## What each feature does
| Feature | What it controls | How it's applied |
| :------------------------------------------------------------------------ | :----------------------------------------------------------------------------------- | :--------------------------------------------------------------------------- |
| [Glossaries](/docs/customize/managing-glossaries) | Exact translations for specific terms, like product names or industry vocabulary | Stored on your account; passed per request via `glossary_id` |
| [Style rules](/docs/customize/using-style-rules) | Formatting conventions (dates, numbers, punctuation) plus stored custom instructions | Stored on your account; passed per request via `style_id` |
| [Custom instructions](/docs/customize/custom-instructions) | Tone, phrasing, and domain-specific behavior via natural-language directives | Inline per request via `custom_instructions`, or stored in a style rule list |
| [Translation memories](/docs/customize/using-translation-memories) | Reuse of your previously approved translations for matching segments | Stored on your account; passed per request via `translation_memory_id` |
| [Spoken terms](/docs/customize/improving-transcription-with-spoken-terms) | Recognition of specific terms during Voice API speech transcription | Stored on your account; passed per voice session via `spoken_terms_id` |
Glossaries, style rules, custom instructions, and translation memories work with both [text translation](/docs/translate/translate-text-quickstart) and [document translation](/docs/translate/translate-documents-quickstart), support all `model_type` values, and can be combined in a single request. Spoken terms apply to [Voice API](/docs/voice/overview) sessions, where they can be combined with glossaries.
Glossaries and style rules are unique to each of DeepL's global data centers and are not shared between them. Clients using [regional endpoints](/docs/getting-started/regional-endpoints) can't access glossaries or style rules created in the UI at this time.
## Choosing the right feature
Here's when to use each customization feature for the best results. The [`context` parameter](/docs/learning-how-tos/examples-and-guides/how-to-use-context-parameter) can also be used to improve translations of ambiguous or short text.
| Use case | Glossaries | Style rules | Custom instructions | Translation memories | Context parameter |
| :------------------------------------------------------- | :--------: | :---------: | :-----------------: | :------------------: | :---------------: |
| **Consistent domain-specific terminology** | ✅ | ❌ | ❌ | ❌ | ❌ |
| **Brand and product names** | ✅ | ❌ | ❌ | ❌ | ❌ |
| **Formatting conventions (dates, numbers, punctuation)** | ❌ | ✅ | ❌ | ❌ | ❌ |
| **Tone and phrasing** | ❌ | ❌ | ✅ | ❌ | ❌ |
| **Reusing previously approved translations** | ❌ | ❌ | ❌ | ✅ | ❌ |
| **Ambiguous words or short snippets** | ❌ | ❌ | ❌ | ❌ | ✅ |
| **Consistent gender or name spelling** | ❌ | ❌ | ❌ | ❌ | ✅ |
## Feature guides
Create, edit, retrieve, and delete glossaries with the v3 endpoints, and use them in translations.
Build style rule lists with configured rules and custom instructions, and apply them via style\_id.
Best practices for writing natural-language instructions that produce consistent results.
Retrieve your translation memories and control the matching threshold in translation requests.
Keep company terms, acronyms, and names transcribed correctly in Voice API sessions.
## API reference
Glossaries, style rules, and translation memories each have management endpoints under the [API Reference](/api-reference/multilingual-glossaries/create-a-glossary). If you're still on the deprecated v2 glossary endpoints, see [Glossary v2 vs v3 Endpoints](/docs/customize/glossary-v2-vs-v3-endpoints) for the differences and migration considerations.
# Spoken Terms Requirements
Source: https://developers.deepl.com/docs/customize/spoken-terms-requirements
Size limits and content rules for Spoken Terms collections and term lists.
## Size limits
| Item | Limit |
| :----------------------- | :------------------------------------------------------- |
| Characters per term list | 300 total |
| Collection name | 1024 UTF-8 bytes |
| Collections per account | Limited by your [plan](https://www.deepl.com/en/pro#api) |
Names using only ASCII characters can be up to 1024 characters long; names with multi-byte characters hold fewer.
## Content rules
* Terms are case-sensitive
* Duplicate terms within a list are not allowed (comparison is case-sensitive)
* Terms must not be empty
* Terms must not contain C0 or C1 control characters (including tabs `\t` or newlines `\n` within a term)
* Terms must not contain leading or trailing whitespace
## Availability
Spoken Terms are available on all plans that include the DeepL Voice API, for all [DeepL Voice languages](https://support.deepl.com/hc/en-us/articles/26625846174364-DeepL-Voice-languages). To check a language programmatically, call [`GET /v3/languages?resource=voice`](/api-reference/languages/retrieve-languages-by-resource) and look for the `spoken_terms` feature key on the source language.
# Using Style Rules
Source: https://developers.deepl.com/docs/customize/using-style-rules
Create style rule lists with configured rules and custom instructions, and apply them to translations with the style_id parameter.
Style rules apply a reusable set of formatting and style conventions to your translations. A **style rule list** combines two types of rules:
* **Configured rules**: predefined options for formatting conventions, like time format, number formatting, and punctuation
* **Custom instructions**: your own natural-language instructions for requirements the predefined rules don't cover
Both are applied together during translation. You can build style rule lists in the UI at [deepl.com/custom-rules](https://deepl.com/custom-rules) or manage them programmatically with the [style rules endpoints](/api-reference/style-rules/list-all-style-rules), as shown below.
The Style Rules API is currently available only to Pro API subscribers.
## Create a style rule list
Send `POST /v3/style_rules` a name, the target `language` the rules apply to, and the rules themselves:
```sh Example request theme={null}
curl -X POST https://api.deepl.com/v3/style_rules \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"name": "Technical Documentation Rules",
"language": "en",
"configured_rules": {
"dates_and_times": {
"calendar_era": "use_bc_and_ad"
},
"punctuation": {
"periods_in_academic_degrees": "do_not_use"
}
},
"custom_instructions": [
{
"label": "Tone instruction",
"prompt": "Use a friendly, diplomatic tone",
"source_language": "en"
}
]
}'
```
```json Example response theme={null}
{
"style_id": "a74d88fb-ed2a-4943-a664-a4512398b994",
"name": "Technical Documentation Rules",
"creation_time": "2024-10-01T12:34:56Z",
"updated_time": "2024-10-01T12:34:56Z",
"language": "en",
"version": 1,
"configured_rules": {
"dates_and_times": {
"calendar_era": "use_bc_and_ad"
},
"punctuation": {
"periods_in_academic_degrees": "do_not_use"
}
},
"custom_instructions": [
{
"id": "68fdb803-c013-4e67-b62e-1aad0ab519cd",
"label": "Tone instruction",
"prompt": "Use a friendly, diplomatic tone",
"source_language": "en"
}
]
}
```
The `version` field increments each time the list is modified, so you can track changes to your style rules. See the [endpoint reference](/api-reference/style-rules/create-style-rule) for all available configured rule categories and options, and the [custom instructions guide](/docs/customize/custom-instructions) for how to write instructions that work well and which kinds of changes they're suited for.
## Apply style rules to a translation
Pass the list's `style_id` in a [`/v2/translate`](/api-reference/translate/request-translation) or [`/v2/document`](/api-reference/document/upload-and-translate-a-document) request. The target language of the request has to match the style rule list's `language`:
```sh Example request theme={null}
curl -X POST https://api.deepl.com/v2/translate \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"text": ["Das Treffen ist um 15:00 Uhr"],
"source_lang": "DE",
"target_lang": "EN",
"style_id": "a74d88fb-ed2a-4943-a664-a4512398b994"
}'
```
All `model_type` values are supported with style rules. Style rules apply to root languages, so a list with `language: "en"` works for `EN-US` and `EN-GB` targets alike; see [How to Apply Customizations to Language Variants](/docs/customize/customizations-for-variants).
## Update a style rule list
As with glossaries, the update methods have different scopes:
* [`PATCH /v3/style_rules/{style_id}`](/api-reference/style-rules/update-style-rule) updates the list's name
* [`PUT /v3/style_rules/{style_id}/configured_rules`](/api-reference/style-rules/update-configured-rules) **replaces all** configured rules; custom instructions are not affected
Custom instructions are managed individually within a list:
* [`POST /v3/style_rules/{style_id}/custom_instructions`](/api-reference/style-rules/create-custom-instruction) adds an instruction
* [`PUT .../custom_instructions/{instruction_id}`](/api-reference/style-rules/update-custom-instruction) replaces one (all fields required)
* [`DELETE .../custom_instructions/{instruction_id}`](/api-reference/style-rules/delete-custom-instruction) removes one
To list or inspect rule lists, use [`GET /v3/style_rules`](/api-reference/style-rules/list-all-style-rules) (add `detailed=true` to include each list's rules and instructions) or [`GET /v3/style_rules/{style_id}`](/api-reference/style-rules/get-style-rule). Deleting a list with [`DELETE /v3/style_rules/{style_id}`](/api-reference/style-rules/delete-style-rule) cannot be undone.
## Limits
* Style rule lists support target languages `de`, `en`, `es`, `fr`, `it`, `ja`, `ko`, and `zh`
* There is no limit on the number of configured rules per list
* A list can hold up to 200 custom instructions (this cap may be adjusted per plan tier in the future); each instruction prompt is limited to 300 characters
* If you need more than 200 custom instructions, split your rules across multiple style rule lists for different content types
# Using Translation Memories
Source: https://developers.deepl.com/docs/customize/using-translation-memories
Learn how to retrieve your translation memories and use them in translation requests
## About translation memories
Translation memories store and reuse previously created translations. When you translate with a translation memory, your source text is compared against the translation memory's segments. If a segment is similar enough, the stored translation is applied instead of generating a new one.
## What you'll learn
In this tutorial, we'll retrieve the translation memories on your account, use one in a translation request, and adjust the matching threshold to control how closely source text must match a stored segment.
## Before you begin
You'll need:
* A DeepL API authentication key
* At least one translation memory uploaded to your account via the DeepL UI
* A terminal or HTTP client for making API requests
* Approximately 10 minutes
### Example translation memory
In this tutorial, we'll use a translation memory called "Greetings" that contains German-to-English translations. One of its segments looks like this:
| Source (DE) | Translation (EN) |
| ----------- | ---------------- |
| Hallo Welt! | Hello everyone! |
Notice that the translation memory specifies a custom translation — "Hello everyone!" instead of the literal "Hello world!". When we send matching source text to the API with this translation memory, DeepL will reuse the stored translation instead of generating its own.
## Step 1: List your translation memories
First, let's retrieve the translation memories on your account so we can find the one we want to use.
```sh theme={null}
curl -X GET 'https://api.deepl.com/v3/translation_memories' \
--header 'Authorization: DeepL-Auth-Key [yourAuthKey]'
```
**Expected output:**
```json theme={null}
{
"translation_memories": [
{
"translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994",
"name": "Greetings",
"source_language": "de",
"target_languages": ["en"],
"segment_count": 42
}
],
"total_count": 1
}
```
Notice the `translation_memory_id` — this is what we'll pass to the translate endpoint. Also note the `source_language` and `target_languages` fields, which tell us this translation memory translates from German to English.
If you see an empty list, make sure you've uploaded at least one translation memory via the [DeepL translation memory page](https://www.deepl.com/translation-memory).
## Step 2: Translate text with a translation memory
Now let's use that translation memory in a translation request. Add the `translation_memory_id` parameter to your call to the [text translation endpoint](/api-reference/translate/request-translation#body-translation-memory-id).
```sh theme={null}
curl -X POST 'https://api.deepl.com/v2/translate' \
--header 'Authorization: DeepL-Auth-Key [yourAuthKey]' \
--header 'Content-Type: application/json' \
--data '{
"text": [
"Hallo Welt!"
],
"target_lang": "EN",
"translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994"
}'
```
**Expected output:**
```json theme={null}
{
"translations": [
{
"detected_source_language": "DE",
"text": "Hello everyone!"
}
]
}
```
The result is "Hello everyone!" — the custom translation from our translation memory — rather than the default "Hello world!" that DeepL would produce without it.
Notice that we didn't need to specify `source_lang` — language auto-detection still works when using a translation memory.
## Step 3: Adjust the matching threshold
By default, a stored segment is applied when it matches at least **75%** of the source text. This means translation memory segments can match even when the source text isn't identical — which is useful for catching typos and minor variations.
Let's see this in action. Imagine we make a typo and send "Halloo Welt!" instead of "Hallo Welt!":
```sh theme={null}
curl -X POST 'https://api.deepl.com/v2/translate' \
--header 'Authorization: DeepL-Auth-Key [yourAuthKey]' \
--header 'Content-Type: application/json' \
--data '{
"text": [
"Halloo Welt!"
],
"target_lang": "EN",
"translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994"
}'
```
**Expected output:**
```json theme={null}
{
"translations": [
{
"detected_source_language": "DE",
"text": "Hello everyone!"
}
]
}
```
With the default threshold of `75`, "Halloo Welt!" is close enough to "Hallo Welt!" that the translation memory segment still applies, and we get our custom translation "Hello everyone!".
Now let's raise the threshold to `100` so only exact matches are used:
```sh theme={null}
curl -X POST 'https://api.deepl.com/v2/translate' \
--header 'Authorization: DeepL-Auth-Key [yourAuthKey]' \
--header 'Content-Type: application/json' \
--data '{
"text": [
"Halloo Welt!"
],
"target_lang": "EN",
"translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994",
"translation_memory_threshold": 100
}'
```
**Expected output:**
```json theme={null}
{
"translations": [
{
"detected_source_language": "DE",
"text": "Hello world!"
}
]
}
```
With a threshold of `100`, "Halloo Welt!" is no longer an exact match for "Hallo Welt!", so the translation memory segment is not applied. DeepL falls back to its default translation: "Hello world!".
## What you've accomplished
You've learned how to:
* Retrieve the translation memories on your account
* Use a translation memory in a translation request
* Control the matching threshold to tune how closely source text must match a stored segment
## See also
* [List translation memories](/api-reference/translation-memory/list-translation-memories) — API reference
* [Text translation endpoint](/api-reference/translate/request-translation#body-translation-memory-id) — `translation_memory_id` and `translation_memory_threshold` parameter reference
* [Supported languages](/docs/getting-started/supported-languages) — check which languages support translation memories
# About
Source: https://developers.deepl.com/docs/getting-started/about
The DeepL API provides programmatic access to DeepL’s language AI technology, making it possible to bring high quality translation capabilities directly to your websites and applications.
## Common use cases for the DeepL API:
* **Website translation**: Localize websites and expand to new markets efficiently and at scale—even in sectors like e-commerce and news media with a large catalog of dynamic content.
* **Company communications**: Integrate DeepL’s translation technology into your company’s systems such as Confluence and SharePoint. Enable your global teams to communicate seamlessly and with [maximum data security](https://www.deepl.com/pro-data-security/).
* **Building multilingual products**: Translate chat conversations to connect users across language barriers in real time. Localize comments and product reviews with the click of a button. Make translation one of your differentiating features—however you imagine it.
In addition, many leading computer-assisted translation (CAT) tool providers have [integrated DeepL’s technology into their software](https://support.deepl.com/hc/articles/360019358599-CAT-tools-supported). This lets translators benefit from DeepL’s high-quality neural translations within their favorite translation tool. If you would like to develop a DeepL plugin for your CAT tool, [please contact us at here](https://support.deepl.com/hc/en-us/requests/new).
## Why the DeepL API?
* **High-quality text and document translations**: DeepL [consistently outperforms the competition](https://www.deepl.com/quality.html) in translation quality—and not only for text translation. The API also supports [many document, publishing, and localization formats](/api-reference/document/upload-and-translate-a-document) including DOCX, PPTX, XLSX, PDF, HTML, IDML, XLIFF, XML, JSON, DITA, and MIF.
* **Maximum data security**: With DeepL API paid plans, texts aren’t saved on persistent storage and aren’t used to train our models. And DeepL adheres strictly to EU data protection laws and ISO 27001. [Learn more about data security at DeepL](https://www.deepl.com/pro-data-security/).
* **Customization with glossaries**: [Specify your own translations for words and phrases](/docs/customize/managing-glossaries), and customize your translations consistently and at scale.
To access the DeepL API, [sign up for a plan](https://www.deepl.com/en/pro#api).
***
**Intended Purpose of the DeepL API**
DeepL API is intended to translate or otherwise process general documents or other content provided by the Customer in accordance with the documentation. DeepL API is not intended for any high-risk applications as defined in [Article 6 of the EU AI Act](https://artificialintelligenceact.eu/article/6/) (including any applicable delegated acts adopted by the European Commission on the basis of this provision).
# SDKs
Source: https://developers.deepl.com/docs/getting-started/client-libraries
Using your favorite programming language with the DeepL API
## Overview
You can use many popular programming languages to access the DeepL API.
DeepL enables this through six official client libraries. [Hosted on GitHub](https://github.com/DeepL), these client libraries handle API requests and help parse responses so you can focus on building your application. For example, [the JavaScript library's `translateDocument()` function](https://github.com/DeepL/deepl-node?tab=readme-ov-file#translating-documents) handles the document translation workflow - uploading a document, polling for completion, and downloading the result.
This documentation site frequently includes code samples in the six programming languages DeepL supports and maintains. But for complete information, setup instructions, installation steps, and code samples, see these GitHub repositories:
deeplcom\deepl-dotnet
deeplcom\deepl-java
deeplcom\deepl-node
deeplcom\deepl-php
deeplcom\deepl-python
deeplcom\deepl-rb
## Community-created client libraries
The DeepL community [maintains client libraries](https://github.com/DeepL/awesome-deepl?tab=readme-ov-file#community-libraries--sdks) for other languages, including [Dart](https://github.com/komape/deepl_dart), [Go](https://github.com/candy12t/go-deepl), [Rust](https://github.com/Avimitin/deepl-rs), and [Kotlin](https://github.com/SimplyMika/DeeplKt).
## Next steps
Now that you've found your client library, here are a few ways to keep learning about the DeepL API:
* try sample requests [in our playground](https://developers.deepl.com/api-reference/translate/request-translation?playground=open)
* [Translate Text Quickstart](/docs/translate/translate-text-quickstart) - send your first text translation requests
* [Translate Documents Quickstart](/docs/translate/translate-documents-quickstart) - translate a complete file, formatting included
# DeepL CLI
Source: https://developers.deepl.com/docs/getting-started/deepl-cli
Install and use the DeepL CLI to translate text, documents, and more from your terminal.
**This page shows you:**
* How to install the DeepL CLI from source
* How to authenticate and run your first translation
* What commands are available for translation, writing, voice, and more
The [DeepL CLI](https://github.com/DeepL/deepl-cli) is an open-source (MIT license) command-line tool for interacting with the DeepL API. It covers text translation, document translation, writing enhancement, voice translation, glossary management, and admin operations — all from your terminal.
## Installation
The CLI requires [Node.js](https://nodejs.org/) (v18+) and build tools for native compilation:
* **macOS**: Xcode Command Line Tools (`xcode-select --install`)
* **Linux**: `python3`, `make`, `gcc` (`apt install python3 make gcc g++`)
* **Windows**: Visual Studio Build Tools
```bash theme={null}
git clone https://github.com/DeepL/deepl-cli.git
cd deepl-cli
npm install
npm run build
npm link
deepl --version
```
## Quick start
### 1. Set up authentication
Use the interactive setup wizard:
```bash theme={null}
deepl init
```
Or set your API key directly:
```bash theme={null}
deepl auth set-key YOUR_API_KEY
```
Or use an environment variable:
```bash theme={null}
export DEEPL_API_KEY=YOUR_API_KEY
```
### 2. Translate text
```bash theme={null}
deepl translate "Hello, world!" --to es
# ¡Hola, mundo!
```
### 3. Enhance your writing
```bash theme={null}
deepl write "Their going to the stor tommorow" --lang en-us
# They're going to the store tomorrow.
```
## Key capabilities
| Command | Description |
| ------------------------ | --------------------------------------------------------------------------- |
| `deepl translate` | Translate text with support for formality, context, and custom instructions |
| `deepl translate --file` | Translate text files while preserving code blocks and formatting |
| `deepl document` | Translate documents (PDF, DOCX, PPTX, XLSX) with format preservation |
| `deepl write` | Enhance grammar and style via DeepL Write |
| `deepl voice` | Stream real-time speech translation via WebSocket |
| `deepl watch` | Monitor files and auto-translate on change |
| `deepl glossary` | Create, list, and manage glossaries |
| `deepl admin` | Manage API keys, usage limits, and team access |
| `deepl usage` | Check API usage and character quotas |
| `deepl config` | Configure defaults (target language, formality, model) |
## Usage examples
### Translate with context and formality
```bash theme={null}
deepl translate "Thank you for your patience" --to de --formality more \
--context "Customer support email to a long-standing client"
```
### Translate a document
```bash theme={null}
deepl document translate report.pdf --to fr --output report-fr.pdf
```
### Batch translate a directory
```bash theme={null}
deepl translate --file src/locales/en/ --to de,fr,es --output src/locales/
```
### Watch mode for development
```bash theme={null}
deepl watch ./content/en --to de,fr --output ./content/
```
### Git hooks integration
Automatically translate changed files before each commit:
```bash theme={null}
deepl hooks install --pre-commit --languages de,fr
```
## Developer workflow features
* Local SQLite cache with LRU eviction avoids redundant API calls
* Monitor billed characters for budget planning with `deepl usage`
* Use `--quiet` and `--no-input` flags for CI/CD pipelines
* Generate shell completions for bash, zsh, fish, and PowerShell
## Further reading
* [DeepL CLI on GitHub](https://github.com/DeepL/deepl-cli) — full documentation, changelog, and source code
* [DeepL API authentication](/docs/getting-started/auth) — set up your API key
* [Client libraries](/docs/getting-started/client-libraries) — official SDKs for six languages
# DeepL MCP Server
Source: https://developers.deepl.com/docs/getting-started/deepl-mcp-server
Use the DeepL MCP Server to add translation capabilities to Claude, Cursor, and other AI agents.
**This page shows you:**
* What the DeepL MCP Server does and when to use it
* How to install and configure it for Claude Code and Claude Desktop
* What tools are available to your AI agent
The [DeepL MCP Server](https://github.com/DeepL/deepl-mcp-server) is an open-source (MIT license) [Model Context Protocol](https://modelcontextprotocol.io/) server that gives AI agents access to DeepL's translation, text improvement, and glossary capabilities. MCP lets AI agents discover and call external tools through a standardized protocol — your agent sends a tool request to the MCP server, which calls the DeepL API and returns the result.
Looking to give your AI tool access to the DeepL documentation itself? See the [Docs MCP Server](/docs/getting-started/docs-mcp-server) for source-grounded answers about the DeepL API.
## Prerequisites
* [Node.js](https://nodejs.org/) v18 or later
* A DeepL API key ([create a free account](https://www.deepl.com/en/pro/change-plan#developer))
## Quick start
Run the server directly with npx:
```bash theme={null}
npx deepl-mcp-server
```
Or install it locally:
```bash theme={null}
npm install deepl-mcp-server
```
## Configuration
Add the MCP server to Claude Code with a single command:
```bash theme={null}
claude mcp add deepl -e DEEPL_API_KEY=your-api-key -- npx deepl-mcp-server
```
Claude Code will now have access to DeepL translation tools in every session.
Add the following to your Claude Desktop configuration file:
* **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
* **Windows**: `%AppData%\Claude\claude_desktop_config.json`
* **Linux**: `~/.config/Claude/claude_desktop_config.json`
```json theme={null}
{
"mcpServers": {
"deepl": {
"command": "npx",
"args": ["deepl-mcp-server"],
"env": {
"DEEPL_API_KEY": "your-api-key"
}
}
}
}
```
Restart Claude Desktop to activate the server.
The DeepL MCP Server works with any MCP-compatible client. Configure it using the `npx deepl-mcp-server` command and pass your API key via the `DEEPL_API_KEY` environment variable.
Refer to your client's documentation for how to add MCP servers.
## Available tools
Once configured, your AI agent can use the following tools:
| **Tool** | **Description** |
| ---------------------- | ------------------------------------------------------------------------------- |
| `translate-text` | Translate text between languages with automatic source language detection |
| `translate-document` | Translate documents (PDF, DOCX, PPTX, XLSX, HTML, TXT) with format preservation |
| `rephrase-text` | Improve and rephrase text with customizable writing style and tone |
| `get-source-languages` | List all available source languages |
| `get-target-languages` | List all available target languages |
| `get-glossary-info` | Retrieve details about a specific glossary |
| `get-glossary-entries` | Fetch dictionary entries from a glossary |
| `list-glossaries` | List all glossaries in your account |
## Example usage
Once the MCP server is connected, you can ask your AI agent things like:
* "Translate this email into German with formal tone"
* "Translate my report.pdf into French"
* "Rephrase this paragraph to sound more professional"
* "What languages does DeepL support?"
* "Show me the entries in my marketing glossary"
The agent will automatically use the appropriate DeepL tool to fulfill the request.
## Next steps
Now that you know how to use the DeepL MCP Server:
* **Explore the source:** Review the [DeepL MCP Server on GitHub](https://github.com/DeepL/deepl-mcp-server) for full documentation and source code
* **Build your own:** Follow the [MCP Server Cookbook](/docs/learning-how-tos/examples-and-guides/deepl-mcp-server-how-to-build-and-use-translation-in-llm-applications) to create a custom MCP server from scratch
* **Set up authentication:** Learn about [DeepL API authentication](/docs/getting-started/auth) and key management
* **Use client libraries:** Explore the [official SDKs](/docs/getting-started/client-libraries) for Python, Node.js, and more
# Docs MCP Server
Source: https://developers.deepl.com/docs/getting-started/docs-mcp-server
Connect your AI tools to the DeepL developer documentation for source-grounded answers about the DeepL API.
**This page shows you:**
* What the DeepL Docs MCP Server is and how it differs from the [DeepL MCP Server](/docs/getting-started/deepl-mcp-server)
* How to connect it to Claude, Cursor, VS Code, and other AI tools
The DeepL developer documentation site exposes a [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server at `https://developers.deepl.com/mcp`. This lets AI tools search and read our documentation directly, so they can give you accurate, up-to-date answers about the DeepL API without relying on training data.
This is different from the [DeepL MCP Server](/docs/getting-started/deepl-mcp-server), which gives AI agents the ability to *call* the DeepL API (translate text, manage glossaries, etc.). The Docs MCP Server provides read-only access to the documentation itself.
## What it provides
When connected, your AI tool gets two capabilities:
* **Search**: Find relevant documentation pages by keyword, returning snippets with titles and links
* **Read**: Navigate and read full pages from the documentation site
Your AI tool decides when to use each capability based on the conversation context. For example, if you ask "How do I use glossaries with the DeepL API?", the tool can search the docs, pull up the relevant pages, and give you an answer grounded in the actual documentation.
## Setup
1. Open Claude Desktop and go to **Settings > Connectors**
2. Click **Add custom connector**
3. Enter a name (e.g., "DeepL Docs") and the URL: `https://developers.deepl.com/mcp`
4. In any chat, click the attachments icon and select the connector to activate it
Run this command to add the server:
```bash theme={null}
claude mcp add --transport http deepl-docs https://developers.deepl.com/mcp
```
Claude Code can now search and reference DeepL documentation during your sessions.
Open Command Palette (`Cmd+Shift+P` / `Ctrl+Shift+P`), search for "Open MCP Settings", and add the following to your `mcp.json`:
```json theme={null}
{
"mcpServers": {
"deepl-docs": {
"url": "https://developers.deepl.com/mcp"
}
}
}
```
Create a `.vscode/mcp.json` file in your project root:
```json theme={null}
{
"servers": {
"deepl-docs": {
"type": "http",
"url": "https://developers.deepl.com/mcp"
}
}
}
```
Any MCP-compatible client can connect using the server URL:
```
https://developers.deepl.com/mcp
```
Refer to your client's documentation for how to add remote MCP servers.
No API key or authentication is required.
## Example prompts
Once connected, try asking your AI tool questions like:
* "How do I translate a document using the DeepL API?"
* "What parameters does the text translation endpoint accept?"
* "How do glossaries work in DeepL?"
* "What are the rate limits for the DeepL API?"
The AI tool will search the documentation and return answers with references to specific pages.
# Quickstart
Source: https://developers.deepl.com/docs/getting-started/quickstart
## Get an API key and get started
New user? Follow these quick steps to get started with the DeepL API.
Visit [our plans page](https://www.deepl.com/pro-api#api-pricing), choose a plan, and sign up.
If you already have a DeepL Translator account, you will need to log out and [create a new account](https://support.deepl.com/hc/articles/360019358999-Change-plan).
Find your API key [here](https://www.deepl.com/your-account/keys).
Then try making a simple translation request.
If you chose a free API plan, replace `https://api.deepl.com` with `https://api-free.deepl.com`.
```http Sample request theme={null}
POST /v2/translate HTTP/2
Host: api.deepl.com
Authorization: DeepL-Auth-Key [yourAuthKey]
User-Agent: YourApp/1.2.3
Content-Length: 45
Content-Type: application/json
{"text":["Hello, world!"],"target_lang":"DE"}
```
```json Sample response theme={null}
{
"translations": [
{
"detected_source_language": "EN",
"text": "Hallo, Welt!"
}
]
}
```
If you chose a free API plan, replace `https://api.deepl.com` with `https://api-free.deepl.com`.
```sh Set the API key theme={null}
export API_KEY={YOUR_API_KEY}
```
```sh Sample request theme={null}
curl -X POST https://api.deepl.com/v2/translate \
--header "Content-Type: application/json" \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--data '{
"text": ["Hello world!"],
"target_lang": "DE"
}'
```
```json Sample response theme={null}
{
"translations": [
{
"detected_source_language": "EN",
"text": "Hallo, Welt!"
}
]
}
```
```sh Install client library theme={null}
pip install deepl
```
```py Sample request theme={null}
import deepl
auth_key = "{YOUR_API_KEY}" # replace with your key
deepl_client = deepl.DeepLClient(auth_key)
result = deepl_client.translate_text("Hello, world!", target_lang="DE")
print(result.text)
```
```text Sample output theme={null}
Hallo, Welt!
```
In production code, it's safer to store your API key in an environment variable.
```sh Install client library theme={null}
npm install deepl-node
```
```javascript Sample request theme={null}
import * as deepl from 'deepl-node';
const authKey = "{YOUR_API_KEY}"; // replace with your key
const deeplClient = new deepl.DeepLClient(authKey);
(async () => {
const result = await deeplClient.translateText('Hello, world!', null, 'de');
console.log(result.text);
})();
```
```text Sample output theme={null}
Hallo, Welt!
```
In production code, it's safer to store your API key in an environment variable.
```sh Install client library theme={null}
composer require deeplcom/deepl-php
```
```php Sample request theme={null}
require_once 'vendor/autoload.php';
use DeepL\Client;
$authKey = "{YOUR_API_KEY}"; // replace with your key
$deeplClient = new DeepL\DeepLClient($authKey);
$result = $deeplClient->translateText('Hello, world!', null, 'de');
echo $result->text;
```
```text Sample output theme={null}
Hallo, Welt!
```
In production code, it's safer to store your API key in an environment variable.
```sh Install client library theme={null}
dotnet add package DeepL.net
```
```csharp Sample request theme={null}
using DeepL; // this imports the DeepL namespace. Use the code below in your main program.
var authKey = "{YOUR_API_KEY}"; // replace with your key
var client = new DeepLClient(authKey);
var translatedText = await client.TranslateTextAsync(
"Hello, world!",
null,
LanguageCode.German);
Console.WriteLine(translatedText);
```
```text Sample output theme={null}
Hallo, Welt!
```
In production code, it's safer to store your API key in an environment variable.
```java Install client library theme={null}
// For instructions on installing the DeepL Java library,
// see https://github.com/DeepL/deepl-java?tab=readme-ov-file#installation
```
```java Sample request theme={null}
import com.deepl.api.*;
public class Main {
public static void main(String[] args) throws DeepLException, InterruptedException {
String authKey = "{YOUR_API_KEY}"; // replace with your key
DeepLClient client = new DeepLClient(authKey);
TextResult result = client.translateText("Hello, world!", null, "de");
System.out.println(result.getText());
}
}
```
```text Sample output theme={null}
Hallo, Welt!
```
In production code, it's safer to store your API key in an environment variable.
```sh Install client library theme={null}
gem install deepl-rb
```
```ruby Sample request theme={null}
require 'deepl'
DeepL.configure do |config|
config.auth_key = '{YOUR_API_KEY}' # replace with your key
end
translation = DeepL.translate 'Hello, world!', nil, 'de'
puts translation.text
```
```text Sample output theme={null}
Hallo, Welt!
```
In production code, it's safer to store your API key in an environment variable.
Pick the product you want to integrate:
Translate text strings and complete documents, with quickstarts for both.
Tailor translations to your domain with glossaries, style rules, and translation memories.
Transcribe and translate spoken audio in real time, starting with the Real-Time Voice Quickstart.
Manage API keys, permissions, and usage across your organization, in the account UI or via the Admin API.
[Our official client libraries](/docs/getting-started/client-libraries) wrap the API for Python, JavaScript, PHP, .NET, Java, and Ruby, and the community maintains [libraries for more languages](https://github.com/DeepL/awesome-deepl?tab=readme-ov-file#community-libraries--sdks), including Dart, Go, and Rust.
## Keep exploring
* [**Cookbook**](/docs/learning-how-tos/cookbook) - Short tutorials, examples, projects, and use cases
* [**Guides**](/docs/learning-how-tos/examples-and-guides) - In-depth explanations for API features and real-world applications
* [**Docs MCP Server**](/docs/getting-started/docs-mcp-server) - Connect your AI tools to this documentation for source-grounded answers
## Community and Support
Support Center
Discord Community
API Status Page
DeepL Status Page (all services)
Release Notes
# Regional API Endpoints
Source: https://developers.deepl.com/docs/getting-started/regional-endpoints
Reference documentation for DeepL's regional API endpoints, including endpoint URLs, configuration, and technical specifications.
DeepL offers regional API endpoints that process and store data within specific geographic regions. Regional endpoints provide the same API functionality as the standard endpoint, with data processing occurring in data centers located in specific regions. These endpoints help organizations meet data residency and compliance requirements, and can also reduce latency for users in specific geographic regions.
Regional endpoints are only available to customers who have signed a regional deployment addendum. Without a signed addendum, requests to regional endpoints return a 403 error. Contact your account manager or [reach out to our sales team](https://www.deepl.com/contact-us) to discuss access.
## Endpoint URLs
DeepL currently offers the following regional endpoints:
| **Region** | **Endpoint URL** |
| ---------------------------- | -------------------------- |
| **United States** | `https://api-us.deepl.com` |
| **Japan** | `https://api-jp.deepl.com` |
| **European Union (default)** | `https://api.deepl.com` |
***
## Configuration
Regional endpoints are configured by specifying the endpoint URL in the API client. The standard endpoint URL (`https://api.deepl.com`) is replaced with the regional endpoint URL (`https://api-us.deepl.com` or `https://api-jp.deepl.com`).
```bash theme={null}
# Standard endpoint
curl -X POST 'https://api.deepl.com/v2/translate' \
--header 'Authorization: DeepL-Auth-Key [yourAuthKey]' \
--header 'Content-Type: application/json' \
--data '{
"text": ["Hello, world!"],
"target_lang": "DE"
}'
# US regional endpoint
curl -X POST 'https://api-us.deepl.com/v2/translate' \
--header 'Authorization: DeepL-Auth-Key [yourAuthKey]' \
--header 'Content-Type: application/json' \
--data '{
"text": ["Hello, world!"],
"target_lang": "DE"
}'
```
The Python client library accepts a `server_url` parameter:
```python theme={null}
import deepl
# Standard endpoint
translator = deepl.Translator("[yourAuthKey]")
# US regional endpoint
translator = deepl.Translator(
"[yourAuthKey]",
server_url="https://api-us.deepl.com"
)
# Usage remains identical
result = translator.translate_text("Hello, world!", target_lang="DE")
print(result.text)
```
The Node.js client library accepts a `serverUrl` option:
```javascript theme={null}
const deepl = require('deepl-node');
// Standard endpoint
const translator = new deepl.Translator('[yourAuthKey]');
// US regional endpoint
const translator = new deepl.Translator(
'[yourAuthKey]',
{ serverUrl: 'https://api-us.deepl.com' }
);
// Usage remains identical
(async () => {
const result = await translator.translateText('Hello, world!', null, 'de');
console.log(result.text);
})();
```
The Java client library accepts a `TranslatorOptions` object with `setServerUrl()` method:
```java theme={null}
import com.deepl.api.*;
// Standard endpoint
Translator translator = new Translator("[yourAuthKey]");
// US regional endpoint
TranslatorOptions options = new TranslatorOptions()
.setServerUrl("https://api-us.deepl.com");
Translator translator = new Translator("[yourAuthKey]", options);
// Usage remains identical
TextResult result = translator.translateText("Hello, world!", null, "de");
System.out.println(result.getText());
```
The .NET client library accepts a `TranslatorOptions` object with `ServerUrl` property:
```csharp theme={null}
using DeepL;
// Standard endpoint
var translator = new Translator("[yourAuthKey]");
// US regional endpoint
var translator = new Translator(
"[yourAuthKey]",
new TranslatorOptions { ServerUrl = "https://api-us.deepl.com" }
);
// Usage remains identical
var result = await translator.TranslateTextAsync("Hello, world!", null, "de");
Console.WriteLine(result.Text);
```
The PHP client library accepts a `server_url` option in the options array:
```php theme={null}
use DeepL\Translator;
// Standard endpoint
$translator = new Translator('[yourAuthKey]');
// US regional endpoint
$translator = new Translator(
'[yourAuthKey]',
['server_url' => 'https://api-us.deepl.com']
);
// Usage remains identical
$result = $translator->translateText('Hello, world!', null, 'de');
echo $result->text;
```
***
## Technical specifications
### Access requirements
Regional endpoints require activation through a regional deployment addendum. Requests to regional endpoints without activation return a 403 error (`Your account is denied access`). Contact your account manager or [reach out to our sales team](https://www.deepl.com/contact-us) to activate regional endpoints for your account.
While API keys are not region-specific, each DeepL account is restricted to a single region. Your keys work only with the regional endpoint your account is assigned to.
### API compatibility
Regional endpoints support all DeepL API functionality except:
* Voice API
* Admin Analytics API
These endpoints are only available on the standard `api.deepl.com` endpoint.
### Glossaries and style rules
Glossaries and style rules are unique to each of DeepL's regional data centers and are not shared between them. Glossaries and style rules created via the API on one regional endpoint (e.g., `api-us.deepl.com`) are only accessible from that same endpoint.
Additionally, the DeepL web UI (at [deepl.com](https://www.deepl.com)) currently only accesses the European Union data center. Glossaries and style rules created in the UI are only accessible via the standard `api.deepl.com` endpoint, not regional endpoints like `api-us.deepl.com` or `api-jp.deepl.com`.
For more details, see the [Style rules documentation](/docs/customize/using-style-rules).
***
## Related documentation
* [Client libraries](/docs/getting-started/client-libraries) - Language-specific client library documentation and configuration
* [Authentication and access](/docs/getting-started/auth) - API authentication methods and security best practices
* [Text translation](/api-reference/translate/request-translation) - Text translation API reference
* [Admin API](/docs/admin/overview) - Programmatic API key management for enterprise administrators
# Languages supported
Source: https://developers.deepl.com/docs/getting-started/supported-languages
The DeepL API supports the following languages.
The DeepL API supports the following languages. These can also be retrieved programmatically via the [`/v3/languages` endpoint](/docs/languages/using-the-languages-api), which returns language support per resource along with feature availability (e.g. formality, glossary, auto-detection). The legacy [`/v2/languages` endpoint](/api-reference/languages/retrieve-supported-languages) is also available but deprecated.
## API Supported Languages
Style rules are supported for the following target languages: `ar`, `bg`, `cs`, `da`, `de`, `el`, `en`, `es`, `et`, `fi`, `fr`, `he`, `hu`, `id`, `it`, `ja`, `ko`, `lt`, `lv`, `nb`, `nl`, `pl`, `pt`, `ro`, `ru`, `sk`, `sl`, `sv`, `th`, `tr`, `uk`, `vi`, and `zh`.
For more details, see the [Style Rules API documentation](/docs/customize/using-style-rules). Writing style and tone availability for `/write/rephrase` can also be retrieved via [`GET /v3/languages?resource=write`](/docs/languages/using-the-languages-api).
# Postman
Source: https://developers.deepl.com/docs/getting-started/test-your-api-requests-with-postman
Use our official Postman collection to get familiar with and test the DeepL API.
Whether you are just getting started with DeepL API, or you already have an integration running in a production environment, sometimes it's a great idea to have a safe test environment to try things out first.
For convenience, we've created a Postman collection that mirrors the DeepL API functionality and lets you test your API in a structured environment. For more information about Postman, [check out their overview](https://learning.postman.com/docs/introduction/overview/).
To get started, [sign up for a developer account at deepl.com](https://www.deepl.com/pro/change-plan#developer) and get your authentication key from [your account page](https://www.deepl.com/your-account/keys).
Click the button below to fork the DeepL API collection into your own Postman workspace:
[](https://app.getpostman.com/run-collection/27518486-e9e2969d-d589-4d3d-9df6-cc514cf3ee5e?action=collection%2Ffork\&source=rip_markdown\&collection-url=entityId%3D27518486-e9e2969d-d589-4d3d-9df6-cc514cf3ee5e%26entityType%3Dcollection%26workspaceId%3D48a52b53-0654-484b-861d-ae228857c2f6)
In your forked collection, select the relevant `baseUrl` variable depending on your subscription (`https://api.deepl.com/v2` for **Pro**, `https://api-free.deepl.com/v2` for **Free**).
The `apiKey` variable will be `DeepL-Auth-Key yourDeepLApiKey`. Change `yourDeepLApiKey` for your own key (leaving the `DeepL-Auth-Key` in front) and you're ready to go!
# Migrating from v2/languages
Source: https://developers.deepl.com/docs/languages/migrating-from-v2-languages
How to migrate from the v2/languages endpoint to the v3/languages endpoints.
This page covers the differences between the `/v2/languages` endpoint and the v3 endpoints, and how to update your integration.
Only `GET` requests are supported on the v3 endpoints. Unlike `/v2/languages`, POST is not supported.
## What changed
### Single endpoint for source and target
v2 uses a single endpoint with a `type` query parameter to distinguish source from target:
```
GET /v2/languages?type=source
GET /v2/languages?type=target
```
v3 uses a single endpoint that returns all languages for a resource, with each language indicating whether it is
usable as a source, a target, or both:
```
GET /v3/languages?resource=translate_text
```
### New resource identifiers
v2 languages are implicitly tied to text and document translation. v3 introduces an explicit `resource` parameter that applies across all DeepL API resources:
| **v2** | **v3 `resource` value** |
| --------------------------------------------------- | ----------------------- |
| *(implicit, text/document translation only)* | `translate_text` |
| *(implicit, text/document translation only)* | `translate_document` |
| *(separate `/v2/glossary-language-pairs` endpoint)* | `glossary` |
| *(not supported)* | `voice` |
| *(not supported)* | `write` |
The v3 endpoints replace both `/v2/languages` and `/v2/glossary-language-pairs`.
### Features instead of `supports_formality`
v2 target languages include a boolean `supports_formality` field. v3 replaces this with a `features` object that covers additional capabilities per resource:
| **v2 field** | **v3 equivalent** |
| ---------------------------- | ------------------------------------------------------------- |
| `"supports_formality": true` | `"formality"` key present in the language's `features` object |
For example, querying languages for text translation:
```sh theme={null}
curl -X GET 'https://api.deepl.com/v3/languages?resource=translate_text' \
--header 'Authorization: DeepL-Auth-Key [yourAuthKey]'
```
```json Example response (truncated) theme={null}
[
{
"lang": "de",
"name": "German",
"usable_as_source": true,
"usable_as_target": true,
"status": "stable",
"features": {
"formality": {"status": "stable"},
"tag_handling": {"status": "stable"},
"glossary": {"status": "stable"}
}
},
{
"lang": "en-US",
"name": "English (American)",
"usable_as_source": false,
"usable_as_target": true,
"status": "stable",
"features": {
"tag_handling": {"status": "stable"},
"glossary": {"status": "stable"}
}
}
]
```
The response indicates German supports `formality` (key present in `features`), but English (American) does not (key absent).
See the [overview](/docs/languages/using-the-languages-api) for the full list of features per resource.
### Response field names
| **v2 field** | **v3 field** |
| -------------------- | -------------------------------------- |
| `language` | `lang` |
| `name` | `name` *(unchanged)* |
| `supports_formality` | `"formality"` key in `features` object |
| *(not present)* | `status` |
### Language code casing
v2 returned language codes in non-standard casing (e.g. `EN-US`, `ZH-HANT`). v3 returns codes compliant with BCP 47: lowercase base language (`en`, `de`), uppercase region subtag (`en-US`, `pt-BR`), and title-case script subtag (`zh-Hant`).
DeepL accepts language codes case-insensitively as input across all endpoints. However, if your integration stores or compares codes returned by `/v2/languages`, update those comparisons to be case-insensitive or to expect the new casing.
## Migrating glossary language pair queries
If you currently use `/v2/glossary-language-pairs` to discover which language pairs are supported for glossaries, use one of the following:
* `GET /v3/languages?resource=glossary` to check which languages support glossary management (i.e. creating a glossary for that language). Filter by `usable_as_source` and `usable_as_target` as needed. Any combination of a valid source and target language is a supported glossary language pair.
* `GET /v3/languages?resource=translate_text` to check which languages support using a glossary during text translation. Languages with a `"glossary"` key in their `features` object support the `glossary_id` parameter on the translate endpoint.
* Similarly, use `resource=translate_document` to check glossary support for document translation.
# Using the Languages API
Source: https://developers.deepl.com/docs/languages/using-the-languages-api
Retrieve supported language and feature data across all DeepL API resources with the v3/languages endpoints, with pseudocode examples for common lookup patterns.
The `/v3/languages` endpoints tell you which languages each DeepL API resource supports and which optional features (formality, glossaries, tag handling, and more) are available per language. Use them to drive language dropdowns, feature toggles, and validation in your integration instead of hardcoding language lists.
The `/v3/languages` endpoints replace the deprecated `/v2/languages` and `/v2/glossary-language-pairs` endpoints.
If you're currently using either, see the [migration guide](/docs/languages/migrating-from-v2-languages) for
differences and code examples.
For the auto-generated API specs, for use with API clients and code generation tools, see:
* [Retrieve languages](/api-reference/languages/retrieve-languages-by-resource)
* [Retrieve resources](/api-reference/languages/retrieve-resources)
To understand how these endpoints are updated when DeepL adds translation support for a new language or language variant, see [the language release process](/docs/resources/language-release-process).
## Resources list
To retrieve language support, decide which DeepL resource you're building for, then call `GET /v3/languages` with
the appropriate `resource` value. The `resource` parameter is required and identifies which DeepL API resource you
are querying language support for:
| **Value** | **Description** |
| -------------------- | ------------------------------------------------------------------ |
| `translate_text` | Text translation via the `/v2/translate` endpoint |
| `translate_document` | Document translation via the `/v2/document` endpoint |
| `voice` | Speech transcription and translation via the `/v3/voice` endpoints |
| `write` | Text improvement via the `/v2/write` endpoints |
| `glossary` | Glossary management via the `/v2/` and `/v3/glossaries` endpoints |
| `style_rules` | Style rules management via the `/v3/style_rules` endpoints |
`glossary` and `style_rules` are resource values indicating glossaries and style rules that can be created for that
language, and managed via the glossary and style rules management endpoints.
Support for glossaries and style rules within specific resources (for example text translation) is indicated by the
`glossary` and `style_rules` feature value, explained in a later section.
## Basic example
Each language in the response includes a `features` object indicating which optional capabilities are available for that
language — see the [Resource features](#resource-features) section below for details.
The examples below use our API Pro endpoint `https://api.deepl.com`. If you're an API Free user, remember to update
your requests to use `https://api-free.deepl.com` instead.
The following example responses are truncated; the full API responses can include over 100 languages.
```sh Example request: languages for text translation theme={null}
curl -X GET 'https://api.deepl.com/v3/languages?resource=translate_text' \
--header 'Authorization: DeepL-Auth-Key [yourAuthKey]'
```
```http Example request: languages for text translation theme={null}
GET /v3/languages?resource=translate_text HTTP/2
Host: api.deepl.com
Authorization: DeepL-Auth-Key [yourAuthKey]
User-Agent: YourApp/1.2.3
```
```json Example response theme={null}
[
{
"lang": "de",
"name": "German",
"usable_as_source": true,
"usable_as_target": true,
"status": "stable",
"features": {
"formality": {"status": "stable"},
"tag_handling": {"status": "stable"},
"glossary": {"status": "stable"}
}
},
{
"lang": "en",
"name": "English",
"usable_as_source": true,
"usable_as_target": false,
"status": "stable",
"features": {
"tag_handling": {"status": "stable"},
"glossary": {"status": "stable"}
}
},
{
"lang": "en-US",
"name": "English (American)",
"usable_as_source": false,
"usable_as_target": true,
"status": "stable",
"features": {
"tag_handling": {"status": "stable"},
"glossary": {"status": "stable"}
}
}
]
```
## Language codes
Language codes in the `lang` field follow [BCP 47](https://www.rfc-editor.org/rfc/rfc5646). The base language
subtag is always present; script, region, and variant subtags are included where needed to distinguish variants. See [Language codes follow BCP 47](/docs/resources/language-release-process#language-codes-follow-bcp-47) for details.
## Resource features
Each language object includes a `features` object indicating which optional capabilities are supported for that language
with the requested resource. Each key is a feature name; the value is an object with at least a `status` field.
To check whether a feature is supported, check that the key exists in the `features` object:
```text theme={null}
// Feature supported:
"features": { "formality": { "status": "stable" } }
// Feature not supported:
"features": {}
```
To use a feature, one or both languages in the pair must support it. For example, for text translation:
* **Target-only**: `formality` only needs to be supported by the target language. Check that `"formality"` is
a key in the target language's `features` object.
* **Source-and-target**: `tag_handling` and `glossary` must be supported by both languages. Check that the
feature key is present in *both* the source and target language's `features` objects.
* **Source-only**: `auto_detection` only needs to be supported by the source language.
In the documentation for API features that are supported for only a subset of languages, we specify
which language feature key to check, and whether to check the source language, target language, or both.
### Resource feature reference
The table below lists all feature keys that can appear in a language's `features` object.
| **Feature** | **Check language support on** | **Resources** | **Description** |
| ------------------- | ----------------------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `auto_detection` | source | `translate_text`, `translate_document`, `voice`, `write` | Language can be automatically detected as the source language. |
| `style_rules` | target | `translate_text` | Language supports style rules that guide how DeepL translates text. Used with the `custom_instructions` and `style_id` parameters on the translate endpoint. |
| `formality` | target | `translate_text`, `translate_document`, `voice` | Language supports formality control — adjusting the output to use formal or informal register. |
| `glossary` | source + target | `translate_text`, `translate_document`, `voice` | Language can be used with a glossary to enforce specific terminology. Both the source and target language must support this for a glossary to be usable with a given pair. |
| `tag_handling` | source + target | `translate_text`, `translate_document` | Language supports tag-aware translation, preserving markup structure (e.g. HTML, XML) in the output. |
| `transcription` | source | `voice` | Language supports transcription from audio to text. |
| `translated_speech` | target | `voice` | Language supports conversion from translated text to audio output. |
| `spoken_terms` | source | `voice` | Language supports spoken terms lists that improve transcription of frequently used terms. Used with the `spoken_terms_id` parameter on the voice request session endpoint. |
| `tone` | target | `write` | Language supports tone selection (e.g. confident, diplomatic, enthusiastic). |
| `writing_style` | target | `write` | Language supports writing style selection (e.g. academic, casual, business). |
## Filtering by availability
By default, `GET /v3/languages` returns only stable languages and features. Use the `include` query parameter
to request additional languages and features based on their availability status:
| **Value** | **Effect** |
| ---------- | -------------------------------------------------------------- |
| `beta` | Includes languages and features in beta, in addition to stable |
| `external` | Includes features that rely on third-party service providers |
Values can be combined with repeated parameters: `?include=beta&include=external`.
The `status` field on each language object and each feature object indicates its availability:
| **Status** | **Meaning** |
| -------------- | --------------------------------- |
| `stable` | Generally available |
| `beta` | Available for testing; may change |
| `early_access` | Limited availability; may change |
## Retrieving resources programmatically
Use the `/v3/languages/resources` endpoint to retrieve the list of resources and their features programmatically.
For each feature, the response indicates which languages must support it for the feature to be available —
source only, target only, or both — allowing clients to determine feature availability for a language pair
by checking the appropriate `features` objects.
```sh theme={null}
curl -X GET 'https://api.deepl.com/v3/languages/resources' \
--header 'Authorization: DeepL-Auth-Key [yourAuthKey]'
```
```json Example response (truncated) theme={null}
[
{
"name": "translate_text",
"features": [
{
"name": "formality",
"needs_target_support": true
},
{
"name": "style_rules",
"needs_target_support": true
},
{
"name": "tag_handling",
"needs_source_support": true,
"needs_target_support": true
},
{
"name": "glossary",
"needs_source_support": true,
"needs_target_support": true
},
{
"name": "auto_detection",
"needs_source_support": true
}
]
}
]
```
## Common use cases
The examples below show how to use the `/v3/languages` endpoints for common integration tasks. They are written
as pseudocode and are resource-agnostic unless otherwise noted.
### Populate source and target language dropdowns
A single call to `GET /v3/languages` returns all languages for a resource. Filter by `usable_as_source` and
`usable_as_target` to populate each dropdown separately.
```
GET /v3/languages?resource=translate_text
languages = response
source_options = languages.filter(l => l.usable_as_source)
target_options = languages.filter(l => l.usable_as_target)
render source_dropdown(source_options)
render target_dropdown(target_options)
```
### Show formality options only when supported
`formality` only needs to be supported by the target language. Check the selected target language's `features`
object — no need to look at the source language.
```
GET /v3/languages?resource=translate_text
languages = response
target = languages.find(l => l.lang == selected_target_lang)
if "formality" in target.features:
show formality_selector // e.g. ["default", "more", "less"]
else:
hide formality_selector
```
### Check if a glossary can be used for a given language pair
`glossary` must be supported by both languages.
```
GET /v3/languages?resource=translate_text
languages = response
source = languages.find(l => l.lang == source_lang)
target = languages.find(l => l.lang == target_lang)
glossary_allowed = "glossary" in source.features
and "glossary" in target.features
```
### List target languages that accept glossaries from a given source language
Filter to targets where both the source and target support the `glossary` feature.
```
GET /v3/languages?resource=translate_text
languages = response
source_lang = "en"
source = languages.find(l => l.lang == source_lang)
if "glossary" not in source.features:
return [] // source doesn't support glossary at all
targets_with_glossary = languages
.filter(l => l.usable_as_target)
.filter(l => "glossary" in l.features)
```
### Show writing style options for the Write resource
`writing_style` is a target-only feature on the `write` resource. Check the target language's `features` object.
```
GET /v3/languages?resource=write
languages = response
target = languages.find(l => l.lang == selected_target_lang)
if "writing_style" in target.features:
show writing_style_selector
else:
hide writing_style_selector
```
### Check if style rules are available for a target language
Use `resource=style_rules` to query which languages support style rules. Style rules are target-language only — check
that the target language is listed in the response. The `style_rules` resource has no additional features, so only
the language availability needs to be checked.
```
GET /v3/languages?resource=style_rules
languages = response
target = languages.find(l => l.lang == selected_target_lang)
if target and target.usable_as_target:
show style_rules_selector
else:
hide style_rules_selector
```
### Determine feature support programmatically
Use `/v3/languages/resources` to drive feature checks at runtime — without hardcoding which features need
target-only or both-language support into your client.
```
GET /v3/languages/resources
GET /v3/languages?resource=translate_text
resources = first response
languages = second response
resource = resources.find(r => r.name == "translate_text")
source = languages.find(l => l.lang == source_lang)
target = languages.find(l => l.lang == target_lang)
for feature in resource.features:
supported = true
if feature.needs_source_support and feature.name not in source.features:
supported = false
if feature.needs_target_support and feature.name not in target.features:
supported = false
```
## API stability
The v3 language endpoints are designed to be forward-compatible:
* New feature keys may be added to the `features` object
* New languages will be added as DeepL support expands
* Existing fields will not be removed or changed in backwards-incompatible ways
In rare cases, a language may be removed from the default response (for example, if it moves from stable
to beta). When this happens, it will still be accessible via `?include=beta`. We aim to avoid this, but
build your integration to handle languages disappearing from the response gracefully.
Build your integration to gracefully handle new BCP 47 `lang` codes and new feature keys in the `features` object. Do not hardcode assumptions about the format of language codes. See [Language codes follow BCP 47](/docs/resources/language-release-process#language-codes-follow-bcp-47) for details.
## Best practices
1. **Cache responses**: Language support changes infrequently. Consider caching responses for up to 1 hour.
2. **Check features**: Always check the `features` object on language objects rather than assuming support (e.g. for formality, glossary use, or writing style).
3. **Handle forward compatibility**: New languages and features may be added at any time. Build your integration to dynamically accept new `lang` codes and new keys in the `features` object instead of maintaining a hardcoded allowlist.
4. **Use specific variants**: For target languages, prefer specific regional variants (e.g., `"en-US"`, `"en-GB"`) when the distinction matters to your users.
# API Usage Logger
Source: https://developers.deepl.com/docs/learning-how-tos/cookbook/api-usage-logger
Learn how to capture per-request DeepL API usage data and visualize it in a local dashboard.
DeepL API Usage Logger on GitHub
This open-source reference project shows how to capture per-request usage data for the DeepL API (billed characters, language pairs, reporting tags, API key identifiers, and errors) and explore it through a local Streamlit dashboard. It wraps the [DeepL Python client](/docs/getting-started/client-libraries) so every text and document translation request is logged to a local DuckDB file as it happens, alongside any errors returned by the API.
The project is intended for teams that need usage reporting with request-level granularity. If you instead want to retrieve subscription-level or API key-level data via a single API call, see the [Usage Analytics Dashboard](/docs/learning-how-tos/cookbook/usage-analytics-dashboard) cookbook, which uses the [Admin API](/api-reference/admin-api/get-usage-analytics).
## Features
* **Per-request logging** for both text and document translation, with the source language, target language, billed characters, and a request ID stored for every call
* **Text translation requests with multiple texts** produce one row per text, all sharing the same `request_id`, so the language and character breakdown of each text within a multi-text request stays visible
* **Error capture alongside successes**, including the error code, HTTP status, and message, so reliability and usage live in the same dataset
* **Reporting tag and API key alias support**, letting you group usage by team, project, or service
* **Streamlit dashboard** with three views: a Usage summary table, an Error summary table, and a SQL Explorer for arbitrary DuckDB queries against the underlying table
* **CSV export** from every dashboard view
* **Non-blocking writes** that queue log entries on a background thread, so logging never adds latency to translation calls
* **Local stack** of Python, DuckDB, and Streamlit, with no external services required
## Screenshots
# Automating Indie Game Localization with the DeepL API and Godot
Source: https://developers.deepl.com/docs/learning-how-tos/cookbook/automating-indie-game-localization-with-the-deepl-api-and-godot
Efficiently translate content for a global audience and widen your game's reach
## Why Automate Game Localization?
As a game developer, reaching a global audience of people who speak other languages can significantly boost your game's visibility, player base, and overall success. Unfortunately, translating game content into multiple languages can easily be out of scope for solo devs, hobbyists, or indie studios. By combining the [open source Godot game engine](https://godotengine.org/) and the DeepL API, you can automate translations, localize your game's UI and dialogue, and easily reach a much wider audience.
In this blog post, we'll explore how to set up an automated translation system within Godot itself. This is a basic workflow example that you'll need to adapt for your own needs, use cases, and project setup, but we hope you can take some inspiration from it.
## Setting Up Your Translation Script
### Godot Script Overview
Our Godot 4.3 script leverages the DeepL API to translate predefined game text into multiple languages. It outputs these translations in a CSV format compatible with Godot, ready for use in your game.
Here's a breakdown of the script, which is written in Godot's built-in GDScript language. You can attach it to any kind of Godot node.
First, specify the languages you want to translate into and the original texts:
```gdscript theme={null}
var languages = [
"DE", "ES", "JA"
]
var originals = [
{"key": "player_greeting", "original": "Hey there, ready for an adventure?", "context": "Player character; Initial interaction"},
{"key": "exit", "original": "Press 'Exit' to leave.", "context": "Instruction; Button label for exiting the game"},
{"key": "score", "original": "Your score is:", "context": "Result; Displayed after completing a level"},
]
```
When the script's node is ready, it initializes the translation process automatically. If you expand this script for your own needs you may want to attach this functionality to a button event so it doesn't run every time the script does.
```gdscript theme={null}
func _ready():
on_translate()
```
These functions set up the translation structure for each language and loop through our list of desired target languages to send the requests to the DeepL API:
```gdscript theme={null}
func on_translate():
for lang in languages:
translations[lang] = {}
start_translations()
func start_translations():
for original in originals:
request_translation(
original["key"],
original["original"],
original["context"]
)
```
For each original text, the script requests translations from the DeepL API. We're passing the additional `context` for each string, as well as the `prefer_less` formality option to maintain a lighthearted and casual tone for our game.
```gdscript theme={null}
func request_translation(key, original_text, context_text):
for lang in languages:
var http_request = HTTPRequest.new()
add_child(http_request)
http_request.connect("request_completed", _on_HTTPRequest_request_completed.bind(key, original_text, lang))
var request_body = {
"text": [original_text],
"target_lang": lang,
"context": context_text,
"formality": "prefer_less"
}
var json = JSON.new()
var json_content = json.stringify(request_body)
var headers = ["Content-Type: application/json", "Authorization: DeepL-Auth-Key " + API_KEY]
http_request.request(API_URL, headers, HTTPClient.METHOD_POST, json_content)
translations_pending += 1
```
Once the API responds, the script processes the translations:
```gdscript theme={null}
func _on_HTTPRequest_request_completed(result, response_code, headers, body, key, original_text, target_lang):
translations_pending -= 1
print("Translation progress: ", originals.size() * languages.size() - translations_pending, "/", originals.size() * languages.size())
if response_code == 200:
var json = JSON.new()
json.parse(body.get_string_from_utf8())
var response = json.get_data()
translations[target_lang][key] = response["translations"][0]["text"]
else:
print("HTTP request failed with response code: ", response_code)
if translations_pending == 0:
save_new_translations()
```
Finally, the script saves translations in a [CSV format that Godot can read natively](https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_translations.html):
```gdscript theme={null}
func save_new_translations():
var file_path = "res://translations/translations.csv"
var file_content = "keys"
for lang: String in languages:
file_content += "," + lang.to_lower()
file_content += "\n"
for original in originals:
file_content += original["key"]
for lang in languages:
var translation = translations[lang][original["key"]]
file_content += ",\"" + translation.replace("\"", "\"\"") + "\""
file_content += "\n"
save_to_file(file_path, file_content)
print("Translations saved to: " + file_path)
```
This will produce a `.csv` file with the following content:
```csv theme={null}
keys,de,es,ja
player_greeting,"Hallo, bist du bereit für ein Abenteuer?","Hola, ¿preparado para una aventura?","やあ、冒険の準備はいいかい?"
exit,"Drücke ""Beenden"", um das Spiel zu verlassen.","Pulsa ""Salir"" para salir.","'Exit'を押して退出してください。"
score,"Dein Ergebnis ist:","Tu puntuación es:","あなたのスコアは:"
```
### Wrapping Up
As you expand your game, consider developing a more sophisticated system to handle larger amounts of content. You could implement batch processing, caching of existing translations, a Godot-based UI for entering and editing original content, advanced error handling, and dynamic language selection.
This is just a quick example of how you can incorporate the DeepL API into a game project to enable effortless translation of your game's dialogue and other text content, allowing even solo developers access to an efficient and scaleable localization process that will enable their projects to reach players all over the world.
# Project: Google Sheets
Source: https://developers.deepl.com/docs/learning-how-tos/cookbook/google-sheets
Open-source example showing how to build a Google Sheets App using the DeepL API.
This sample explains in detail how an App for Google Sheets can be built to integrate DeepL translations into Google Sheets. This also works quite similarly for Google Docs!
Google Sheet Example on Github
# Building a Document Translator with the DeepL API
Source: https://developers.deepl.com/docs/learning-how-tos/cookbook/java-document-translator
Streamline document localization workflows with automated translation for businesses and developers
## Why Build a Document Translator?
In today's global business environment, organizations frequently need to translate various document types - from technical manuals and legal contracts to marketing materials and internal communications. Manually uploading files to web-based translation services can be time-consuming and inefficient, especially when dealing with multiple documents.
By building a command-line Java application with the DeepL API, you can automate document translation processes, integrate them into CI/CD pipelines, and provide a reliable solution for bulk document processing. This approach is particularly valuable for:
* **Development teams** who need to localize documentation and user manuals
* **Content teams** managing multilingual marketing materials
* **Businesses** requiring regular translation of contracts, reports, and communications
* **Automation workflows** where translation needs to be triggered programmatically
## Setting Up Your Document Translator
### Prerequisites
Before you begin, you'll need:
* Java Development Kit (JDK) 8 or higher
* Apache Maven for dependency management
* A DeepL API key (get one at [DeepL API](https://www.deepl.com/pro-api))
* Basic familiarity with Java and command-line tools
### Project Overview
Our Java application leverages the official DeepL Java SDK to translate documents across multiple formats. It provides a simple command-line interface that takes an input file and target language, automatically generating appropriately named output files.
Here's the complete implementation breakdown:
#### 1. Project Setup and Dependencies
First, let's set up the Maven project structure with the necessary dependencies:
```xml theme={null}
4.0.0
com.example
java-document-translator
1.0-SNAPSHOT
1.8
1.8
UTF-8
com.deepl.api
deepl-java
1.10.0
org.codehaus.mojo
exec-maven-plugin
3.0.0
App
```
The `deepl-java` dependency provides all the necessary functionality for interacting with the DeepL API, including document translation capabilities.
#### 2. Supported File Types Definition
We start by defining the supported file formats to provide clear feedback to users:
```java theme={null}
private static final Map SUPPORTED_EXTENSIONS;
static {
Map map = new HashMap<>();
map.put("docx", "Microsoft Word Document");
map.put("doc", "Microsoft Word Document");
map.put("pptx", "Microsoft PowerPoint Document");
map.put("xlsx", "Microsoft Excel Document");
map.put("pdf", "Portable Document Format");
map.put("htm", "HTML Document");
map.put("html", "HTML Document");
map.put("txt", "Plain Text Document");
map.put("xlf", "XLIFF Document (1.2 / 2.0 / 2.1)");
map.put("xliff", "XLIFF Document (1.2 / 2.0 / 2.1)");
map.put("srt", "SubRip Subtitle file");
map.put("idml", "Adobe InDesign Markup Language");
map.put("xml", "XML Document");
map.put("json", "JSON Document");
map.put("dita", "DITA topic");
map.put("mif", "Adobe FrameMaker Interchange Format");
SUPPORTED_EXTENSIONS = Collections.unmodifiableMap(map);
}
```
This static initialization ensures our application can quickly validate file types before attempting translation, providing immediate feedback for unsupported formats.
#### 3. Command Line Argument Processing
The application expects two command-line arguments: the input file path and target language code. The source language is automatically detected by the DeepL API.
```java theme={null}
public static void main(String[] args) {
String inputFilePath = args[0];
String targetLang = args[1];
// Validate file extension
String extension = "";
int i = inputFilePath.lastIndexOf('.');
if (i > 0 && i < inputFilePath.length() - 1) {
extension = inputFilePath.substring(i + 1);
String lowerCaseExtension = extension.toLowerCase();
if (SUPPORTED_EXTENSIONS.containsKey(lowerCaseExtension)) {
System.out.println("File type: " + SUPPORTED_EXTENSIONS.get(lowerCaseExtension));
} else {
System.err.println("Error: Unsupported file extension '" + extension + "'");
return;
}
}
}
```
#### 4. Output File Path Generation
One key feature is automatic output file naming, which prevents accidental overwrites and clearly identifies translated versions:
```java theme={null}
// Auto-generate output file path
Path inputPathObject = Paths.get(inputFilePath);
String originalFileName = inputPathObject.getFileName().toString();
String newFileName = targetLang.toUpperCase() + "_" + originalFileName;
Path outputFilePathObject = inputPathObject.resolveSibling(newFileName);
String outputFilePath = outputFilePathObject.toString();
```
This approach transforms `document.pdf` with target language `DE` into `DE_document.pdf`, making it easy to identify translated versions.
#### 5. DeepL API Integration
The core translation functionality uses the DeepL Java SDK:
```java theme={null}
// Initialize DeepL client
String authKey = System.getenv("DEEPL_AUTH_KEY");
if (authKey == null || authKey.isEmpty()) {
System.err.println("Error: DEEPL_AUTH_KEY environment variable not set.");
return;
}
File inputFile = Paths.get(inputFilePath).toFile();
File outputFile = Paths.get(outputFilePath).toFile();
DeepLClient client = new DeepLClient(authKey);
// Perform translation
DocumentStatus status = client.translateDocument(inputFile, outputFile, null, targetLang);
System.out.println("Document translation initiated. Document ID: " + status.getDocumentId());
```
#### 6. Translation Status Monitoring
Document translation is asynchronous, so we need to monitor the process:
```java theme={null}
System.out.println("Waiting for translation to complete...");
while (true) {
StatusCode statusCode = status.getStatus();
if (statusCode == StatusCode.Done) {
System.out.println("Translation completed successfully.");
break;
}
if (statusCode == StatusCode.Error) {
System.err.println("Error during translation: " + status.getErrorMessage());
break;
}
Thread.sleep(1000); // Wait 1 second before checking status again
}
```
This polling mechanism ensures the application waits for translation completion and provides appropriate feedback.
### Building and Running
1. **Set up your environment:**
```bash theme={null}
export DEEPL_AUTH_KEY="your_deepl_api_key_here"
```
2. **Build the project:**
```bash theme={null}
mvn compile
```
3. **Run translations:**
Here are some practical examples of using the translator:
**Translating Technical Documentation:**
```bash theme={null}
mvn exec:java -Dexec.args="./api-documentation.pdf EN-US"
```
**Localizing Marketing Materials:**
```bash theme={null}
mvn exec:java -Dexec.args="./brochure.docx JA"
```
**Processing Subtitle Files:**
```bash theme={null}
mvn exec:java -Dexec.args="./movie-subtitles.srt DE"
```
### Wrapping Up
This Java document translator provides a solid foundation for automating document localization workflows. By combining the reliability of Java with DeepL's translation quality, you can build scalable solutions for various business needs.
The command-line interface makes it easy to integrate into existing automation scripts, CI/CD pipelines, or batch processing workflows. Whether you're a developer localizing documentation or a business automating multilingual content creation, this approach offers a practical solution for programmatic document translation.
The extensible design allows for easy customization and enhancement, making it adaptable to specific organizational requirements while maintaining the core functionality of reliable, high-quality document translation.
## References
* [DeepL API Documentation](https://developers.deepl.com/docs)
* [DeepL Java SDK](https://github.com/DeepL/deepl-java)
* [Maven Exec Plugin](https://www.mojohaus.org/exec-maven-plugin/)
* [DeepL Supported Languages](https://www.deepl.com/docs-api/translate-text/target-language/)
# Making API calls from client-side JavaScript
Source: https://developers.deepl.com/docs/learning-how-tos/cookbook/nodejs-proxy
Two lightweight local proxies that let you try the DeepL API directly from a website
These proxies are intended for prototyping and frontend testing. For production environments, consider implementing your own backend service with additional security measures and rate limiting.
## Rationale
The DeepL API does not permit calls from client-side JavaScript - that is, from JavaScript running in a browser. This policy exists to keep your API key safe. When you place a call from your website directly to the DeepL API, that call needs to include your API key. Anyone using your website could look at the network calls or your code itself, find your API key, and use it themselves.
For this and other security considerations, DeepL [does not enable the CORS headers](/docs/best-practices/cors-requests) you would need to call the API from a webpage hosted on your origin. The industry-standard approach is to call APIs like DeepL’s from a back-end service. DeepL enforces this best practice to keep your credentials secure.
## When to use a quick proxy
But what do you do if you want to use the API quickly, in a prototype, a demo, or a hackathon, where you need to get up and running fast? What if you don’t have easy access to a server?
For such situations, here are two solutions.
## Node.js Proxy Server
DeepL API Node.js Proxy on GitHub
This full-featured proxy server supports all DeepL API endpoints, including text translation, document translation, and glossaries. Built with Node.js and Express with minimal dependencies, it handles CORS headers for you and keeps your API key secure. It even includes an interactive web demo for testing translations right in your browser.
This proxy includes an interactive setup wizard to get you started quickly. Clone the repo, run the setup, and start sending requests from your browser. Check out the [GitHub repository](https://github.com/DeepL/deepl-api-nodejs-proxy) for full setup instructions and Docker support.
## Quick & dirty PHP proxy
If you’re just using our `/translate` endpoint and want an even quicker solution, or if you don’t use node.js, you could use the PHP code below to create an instant proxy server. If PHP is not already installed on your machine, you can download and install it at [php.net](https://www.php.net/downloads.php).
The PHP script takes the parameters in the query string and passes them into a `POST` request to `/translate`. It does not check that the parameters are valid and does not support any other endpoints, although you could modify it to do so.
To use this while developing on your local machine:
* replace `{your API key here}` with your actual API key
* create a file in your favorite directory with the PHP code below
* go to your terminal and visit that directory
* type `php -S localhost:8000`
Your JavaScript can then access the DeepL API at `http://localhost:8000/php-proxy.php`. For example, to send a translation request with `text` and `target_lang`:
```javascript theme={null}
const url = `http://localhost:8000/deepl-proxy.php?text=${encodeURIComponent(text)}&target_lang=${encodeURIComponent(targetLang)}`;
const response = await fetch(url);
const result = await response.text();
```
Here is the PHP script:
```php theme={null}
[
'method' => 'POST',
'header' => "Authorization: DeepL-Auth-Key " . $apiKey . "\r\n" .
"Content-Type: application/x-www-form-urlencoded\r\n",
'content' => $data
]
]);
echo file_get_contents($apiUrl, false, $context);
?>
```
## Screenshots
### Node.js proxy
### PHP proxy
# Sending Custom Reporting Tags with Client Libraries
Source: https://developers.deepl.com/docs/learning-how-tos/cookbook/sending-custom-reporting-tags-from-client-libraries
Code snippets for attaching X-DeepL-Reporting-Tag to translate requests using the official DeepL client libraries.
This recipe shows how to attach a custom reporting tag to translate requests using each official DeepL client library. For an overview of what custom reporting tags are, how they're stored, and how to query their usage, see [How to Use Custom Reporting Tags](/docs/learning-how-tos/examples-and-guides/how-to-use-custom-reporting-tags).
## Recommended pattern: one client per tag
The Python, Node.js, Java, .NET, and PHP client libraries let you attach custom HTTP headers when constructing a `DeepLClient`. The simplest way to tag your requests is to construct one `DeepLClient` per tag value, configure it with `X-DeepL-Reporting-Tag` once, and reuse it for every request that should carry that tag.
Each `DeepLClient` instance owns its own HTTP connection pool, so keep instances around and reuse them rather than constructing a new client per request.
The Ruby library (`deepl-rb`, v3.8.0 and later) uses a module-level API and accepts an `additional_headers` argument on each `translate` call instead of a client-level option. Pass `X-DeepL-Reporting-Tag` on every call you want to tag.
## Code snippets
The snippets below all send the same translate request (`"Welcome to your dashboard." → DE`) and tag it as `team-billing`. Replace `YOUR_AUTH_KEY` with your DeepL API authentication key.
```python theme={null}
import deepl
client = deepl.DeepLClient("YOUR_AUTH_KEY")
client.headers["X-DeepL-Reporting-Tag"] = "team-billing"
result = client.translate_text(
"Welcome to your dashboard.",
target_lang="DE",
)
print(result.text)
```
The Python library exposes `client.headers` as a public dictionary. Setting the key after construction merges with the library-managed `Authorization` and `User-Agent` headers.
```typescript theme={null}
import * as deepl from 'deepl-node';
const client = new deepl.DeepLClient('YOUR_AUTH_KEY', {
headers: { 'X-DeepL-Reporting-Tag': 'team-billing' },
});
const result = await client.translateText(
'Welcome to your dashboard.',
null,
'DE',
);
console.log(result.text);
```
The `headers` option on `DeepLClientOptions` is merged with the library-managed `Authorization` and `User-Agent` headers at construction time.
```java theme={null}
import com.deepl.api.*;
import java.util.Map;
DeepLClientOptions options = new DeepLClientOptions()
.setHeaders(Map.of("X-DeepL-Reporting-Tag", "team-billing"));
DeepLClient client = new DeepLClient("YOUR_AUTH_KEY", options);
TextResult result = client.translateText(
"Welcome to your dashboard.",
null,
"DE"
);
System.out.println(result.getText());
```
`DeepLClientOptions.setHeaders` accepts any `Map`. The configured headers are sent on every request issued by this client.
```csharp theme={null}
using DeepL;
var options = new DeepLClientOptions {
Headers = new Dictionary {
{ "X-DeepL-Reporting-Tag", "team-billing" }
}
};
var client = new DeepLClient("YOUR_AUTH_KEY", options);
var result = await client.TranslateTextAsync(
"Welcome to your dashboard.",
null,
"DE"
);
Console.WriteLine(result.Text);
```
`DeepLClientOptions.Headers` is a `Dictionary`. Headers configured here are attached to every request the client makes.
```php theme={null}
[
'X-DeepL-Reporting-Tag' => 'team-billing',
],
]);
$result = $client->translateText(
'Welcome to your dashboard.',
null,
'DE'
);
echo $result->text;
```
The `headers` option in the constructor's options array is merged with the library-managed `Authorization` and `User-Agent` headers.
```ruby theme={null}
require 'deepl'
DeepL.configure do |config|
config.auth_key = 'YOUR_AUTH_KEY'
end
additional_headers = { 'X-DeepL-Reporting-Tag' => 'team-billing' }
result = DeepL.translate(
'Welcome to your dashboard.',
'EN',
'DE',
{},
additional_headers
)
puts result.text
```
`additional_headers` is the fifth positional argument on `DeepL.translate`. Pass an empty options hash (`{}`) before it when you don't need any other translate options. The headers are merged with the library-managed `Authorization` and `User-Agent` headers on each request. Requires `deepl-rb` v3.8.0 or later.
**Output (all snippets):**
```
Willkommen in Ihrem Dashboard.
```
To confirm `X-DeepL-Reporting-Tag` is leaving your client correctly, point the SDK at [deepl-mock](https://github.com/DeepL/deepl-mock) via the `server_url` / `serverUrl` option and inspect the headers it logs. Useful for catching configuration mistakes before sending real tagged traffic.
## Tagging across many values
The one-client-per-tag pattern works well when your tag dimension is bounded: teams, products, or a handful of business units. If you need to tag with high-cardinality values, such as a unique tag per end customer, holding a `DeepLClient` instance for every value isn't practical.
Today, none of the client libraries expose per-request custom headers through their public translate methods. The HTTP client layers in deepl-node, deepl-python, and deepl-php already support per-request header injection internally, so the foundation is in place. The Java and .NET libraries support only per-instance headers.
Updates to extend per-request custom-header support across the client libraries are planned. Until those updates land, the per-instance pattern above is the supported path.
## Next steps
* **Concepts and limits:** Read [How to Use Custom Reporting Tags](/docs/learning-how-tos/examples-and-guides/how-to-use-custom-reporting-tags) for the validation rules, naming guidance, and the relationship between tags and API keys.
* **Query tagged usage:** See the [Get custom tag usage analytics](/api-reference/admin-api/get-custom-tag-usage-analytics) reference.
# Usage Analytics Dashboard
Source: https://developers.deepl.com/docs/learning-how-tos/cookbook/usage-analytics-dashboard
A demo dashboard for visualizing DeepL API key usage across an entire account using the Admin API.
Usage Analytics Demo Dashboard on GitHub
This open-source demo dashboard shows how to visualize DeepL API key usage across your entire account, powered by a single API call to the `/v2/admin/analytics` endpoint. It provides interactive charts, flexible date ranges, and per-API-key breakdowns to help you monitor and analyze your API usage.
The dashboard is designed to be lightweight and easy to set up, with zero NPM dependencies and sample data included for testing. It can be used as-is, or as an example of how similar data can be incorporated into your own internal workflows and dashboards.
For more information about the DeepL Admin API endpoint, check out the [Admin API documentation](/api-reference/admin-api/get-usage-analytics).
If you need per-request logging instead of account-wide views, see the [API Usage Logger](/docs/learning-how-tos/cookbook/api-usage-logger) cookbook.
## Features
* **Interactive charts and visualizations** - View your usage data through multiple chart types
* **Flexible date ranges** - Analyze usage over rolling periods or fixed date ranges
* **Per-API-key breakdown** - See usage broken down by individual API keys
* **Basic trends and forecasting** - Identify patterns in your API usage
* **Secure local configuration** - All configuration stays on your machine
* **Zero NPM dependencies** - Chart.js loaded from CDN for simplicity
* **Sample data included** - Test and demo without connecting to your account
## Screenshots
# DeepL MCP Server: How to build and use translation in LLM applications
Source: https://developers.deepl.com/docs/learning-how-tos/examples-and-guides/deepl-mcp-server-how-to-build-and-use-translation-in-llm-applications
Efficiently translate content with AI-powered translation and enhance your interactions with large language models.
This cookbook is intended for developers who want to learn how to build an MCP server using the DeepL API. If you're looking to simply use a pre-built DeepL MCP server without building it yourself, please go directly to the [GitHub repository](https://github.com/DeepL/deepl-mcp-server) for installation instructions.
Large Language Models excel at many tasks but may not provide optimal translations for all languages. By combining the DeepL API with the Model Context Protocol (MCP), you can provide Claude and other MCP-compatible clients with access to DeepL's specialized translation capabilities, bringing in the ability to provide translations across numerous languages.
In this cookbook, we'll explore how to create an MCP server that connects DeepL's translation API with clients like Claude Desktop, GitHub Copilot, and any other clients that work with MCPs! This allows you to seamlessly translate text between languages within your conversations. To look at the code and start using it, go to [GitHub](https://github.com/DeepL/deepl-mcp-server).
### What is MCP?
The Model Context Protocol (MCP) is an open standard introduced by Anthropic that standardizes how AI applications connect with external tools, data sources, and systems. Think of it as a "USB for AI integrations" – it provides a universal adapter between AI applications and external data sources through a standardized interface.
This elegant design solves the integrations problem: instead of building custom connectors between each AI application and each external tool, developers only need to implement the MCP standard once on each side, reducing integration complexity. In the context of the DeepL MCP Server, MCP allows AI assistants to seamlessly access DeepL's specialized translation capabilities while maintaining a consistent, secure communication protocol, combining the strengths of both systems to deliver a better User Experience.
### Setting Up Your DeepL MCP Server
#### Prerequisites
Before you begin, you'll need:
* A DeepL API key (get one at [DeepL API](https://www.deepl.com/pro-api))
* Node.js installed on your system
* Basic familiarity with JavaScript/Node.js
First, let's set up a new Node.js project:
```bash theme={null}
# Create a new directory for our project
mkdir deepl-mcp-server
cd deepl-mcp-server
# Initialize npm project
npm init -y
# Install dependencies
npm install @modelcontextprotocol/sdk deepl-node zod
```
Here's what each dependency does:
* `@modelcontextprotocol/sdk`: The MCP SDK that allows our server to communicate with MCP clients like Claude Desktop
* `deepl-node`: Official DeepL API client for Node.js, making it easy to interact with DeepL's translation services
* `zod`: A TypeScript-first schema validation library that we'll use to define our tool parameters
The Model Context Protocol (MCP) enables AI systems to access external tools, providing them with specialized capabilities beyond their built-in functionality. For translation tasks, this is particularly valuable as it combines DeepL's translation expertise with Claude's conversational abilities.
The `McpServer` class is the core of our implementation. It handles all the protocol-specific details of communicating with MCP clients. The `StdioServerTransport` uses standard input/output streams for communication, which works well with Claude Desktop's execution model where it spawns separate processes for each server.
We're using environment variables to pass the DeepL API key to our server, which is a secure way to handle sensitive credentials. Create a file named `src/index.mjs` with the following structure:
```javascript theme={null}
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import * as deepl from 'deepl-node';
// The DeepL API Key is passed in as a part of the client configuration
const DEEPL_API_KEY = process.env.DEEPL_API_KEY;
const translator = new deepl.Translator(DEEPL_API_KEY);
// Create server instance
const server = new McpServer({
name: "deepl", // The name clients will use to identify this server
version: "0.1.0-beta.0", // Version for compatibility checks
capabilities: {
resources: {},
tools: {},
},
});
// Server implementation goes here
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("DeepL MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
```
Let's add some helper functions to manage language lists and validation. These helper functions serve several important purposes:
1. **Caching**: We cache the language lists to avoid unnecessary API calls, as these lists rarely change and DeepL has API usage limits.
2. **Validation**: Before sending requests to DeepL, we validate that the requested language codes are supported. This provides better error messages to users and prevents unnecessary API calls with invalid parameters.
3. **Separation of concerns**: By extracting these functions, we keep our tool implementations clean and focused on their primary purpose.
The DeepL API distinguishes between source languages (languages you can translate from) and target languages (languages you can translate to), with slightly different sets of supported languages for each direction. Our implementation respects this distinction.
```javascript theme={null}
// Cache for language lists
let sourceLanguagesCache = null;
let targetLanguagesCache = null;
// Helper function to validate languages
async function validateLanguages(sourceLang, targetLang) {
const sourceLanguages = await getSourceLanguages();
const targetLanguages = await getTargetLanguages();
if (sourceLang && !sourceLanguages.some(lang => lang.code === sourceLang)) {
throw new Error(`Invalid source language: ${sourceLang}. Available languages: ${sourceLanguages.map(l => l.code).join(', ')}`);
}
if (!targetLanguages.some(lang => lang.code === targetLang)) {
throw new Error(`Invalid target language: ${targetLang}. Available languages: ${targetLanguages.map(l => l.code).join(', ')}`);
}
}
// Helper functions to get languages
async function getSourceLanguages() {
if (!sourceLanguagesCache) {
sourceLanguagesCache = await translator.getSourceLanguages();
}
return sourceLanguagesCache;
}
async function getTargetLanguages() {
if (!targetLanguagesCache) {
targetLanguagesCache = await translator.getTargetLanguages();
}
return targetLanguagesCache;
}
```
Let's define one of the most important tools our server will provide - the translation tool. For brevity, we'll only show one implementation, but the complete code includes additional tools like `get-source-languages`, `get-target-languages`, and `rephrase-text`.
```javascript theme={null}
server.tool(
"translate-text",
"Translate text to a target language using DeepL API",
{
text: z.string().describe("Text to translate"),
sourceLang: z.string().nullable().describe("Source language code (e.g. 'en', 'de', null for auto-detection)"),
targetLang: z.string().describe("Target language code (e.g. 'en-US', 'de', 'fr')"),
formality: z.enum(['less', 'more', 'default', 'prefer_less', 'prefer_more']).optional().describe("Controls whether translations should lean toward informal or formal language"),
},
async ({ text, sourceLang, targetLang, formality }) => {
try {
// Validate languages before translation
await validateLanguages(sourceLang, targetLang);
const result = await translator.translateText(
text,
sourceLang,
targetLang,
{ formality }
);
return {
content: [
{
type: "text",
text: result.text,
},
{
type: "text",
text: `Detected source language: ${result.detectedSourceLang}`,
},
],
};
} catch (error) {
throw new Error(`Translation failed: ${error.message}`);
}
}
);
```
For the complete implementation of all tools, including `get-source-languages`, `get-target-languages`, and `rephrase-text`, please refer to the [GitHub repository](https://github.com/DeepL/deepl-mcp-server).
### Connecting to Claude Desktop
To use your DeepL MCP server with Claude Desktop:
1. Create or edit the Claude Desktop configuration file:
* On macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
* On Windows: `%AppData%\Claude\claude_desktop_config.json`
* On Linux: `~/.config/Claude/claude_desktop_config.json`
2. Add your DeepL MCP server configuration:
```json theme={null}
{
"mcpServers": {
"deepl": {
"command": "node",
"args": [
"/path/to/deepl-mcp-server/src/index.mjs"
],
"env": {
"DEEPL_API_KEY": "your-api-key-here"
}
}
}
}
```
3. Replace `/path/to/deepl-mcp-server` with the actual path to your server directory
4. Replace `your-api-key-here` with your actual DeepL API key
5. Restart Claude Desktop
In case it worked, deepl should show up as one of the available tools within the "Search and Tools" menu.
### Testing Your Server
You can test your server's capabilities by asking Claude Desktop translation-related questions:
* "Can you translate 'Hello, how are you?' to German using DeepL?"
* "Please translate this paragraph to Japanese using DeepL: \[your text here]"
* "Can you rephrase this text in a more formal way using DeepL: \[your text here]"
* "What languages can you translate from and to using DeepL?"
Although it isn't an exact science, mentioning the keyword "DeepL" helps Claude understand that we want to use that tool during the interaction.
### Understanding the Code
Let's break down the key components of our implementation:
#### Server Initialization
The `McpServer` class creates an MCP-compatible server that exposes tools to clients:
```javascript theme={null}
const server = new McpServer({
name: "deepl",
version: "0.1.0-beta.0",
capabilities: {
resources: {},
tools: {},
},
});
```
#### Tool Definition
Each tool follows a similar pattern:
1. Name of the tool
2. Description of what it does
3. Schema for parameters (using Zod)
4. Implementation function
For example, the `translate-text` tool:
```javascript theme={null}
server.tool(
"translate-text", // Name
"Translate text using DeepL API", // Description
{ // Parameters schema
text: z.string().describe("Text to translate"),
// ...more parameters
},
async ({ text, sourceLang, targetLang, formality }) => {
// Implementation
}
);
```
#### Transport Setup
The `StdioServerTransport` enables communication between the MCP server and clients:
```javascript theme={null}
const transport = new StdioServerTransport();
await server.connect(transport);
```
### Wrapping Up
By following this cookbook, you've created an MCP server that enables Claude Desktop and other MCP-compatible clients to access DeepL's translation capabilities. This allows seamless translation within your conversations, improving the multilingual capabilities of your AI interactions.
As you expand your server, consider adding more features like:
* Support for document translation
* Custom glossaries for domain-specific terminology
* Batch processing for multiple translations
* Caching to improve performance and reduce API usage
The combination of specialized AI services (like DeepL) with general-purpose AI assistants (like Claude) through MCP creates powerful workflows that combine the strengths of different AI systems.
### References
DeepL API
Model Context Protocol
DeepL MCP Server GitHub
Claude Desktop MCP Configuration
# How to Use the Context Parameter Effectively
Source: https://developers.deepl.com/docs/learning-how-tos/examples-and-guides/how-to-use-context-parameter
Learn when and how to use the context parameter to improve translation accuracy for ambiguous content.
**This guide shows you:**
* When to use `context` (and when not to)
* How to use `context` to resolve ambiguous words, genders, or transliterations
* Where to find a comparison of `context` with DeepL's customization features
***
## What the context parameter is for
The `context` parameter helps DeepL's API translate ambiguous words and short text snippets more accurately by providing the surrounding content. Think of it like showing a human translator the paragraphs before and after the sentence being translated.
The `context` parameter can help with:
* Picking the correct translation for ambiguous words
* Providing grammatical clues for gender, number, or case that isn't clear from the text alone
* Improving translations of short snippets such as headlines or product names
## What the context parameter is NOT for
**Common Misconception:** Many users try to use `context` like ChatGPT system prompts. **This does not work reliably.**
The `context` parameter is **not** designed for:
* LLM-style instructions: "Translate with a friendly, casual tone"
* Translation rules: "Always translate 'Tor' as 'gate'"
* Cultural context: "Adapt for German cultural norms"
Using `context` like this will produce unpredictable results. The parameter is optimized for document content, not commands.
Instead, try:
* [Style rules and custom instructions](/docs/customize/using-style-rules): For tone, style, formatting, and translation instructions
* [Glossaries](/docs/customize/managing-glossaries): For consistent terminology and brand names
***
## How to use context for ambiguous words
When a word has multiple meanings, surrounding context helps the translation engine choose the correct interpretation.
### Example
"Tor" could mean "gate" or "goal" in German. Without context, DeepL may not know which meaning you intend:
```sh theme={null}
curl -X POST 'https://api.deepl.com/v2/translate' \
--header 'Authorization: DeepL-Auth-Key [your key]' \
--header 'Content-Type: application/json' \
--data '{
"text": [
"Die Person stand vor dem Tor."
],
"target_lang": "EN-US"
}'
```
**Output without context:**
```text theme={null}
"The person was standing in front of the gate."
```
If you're writing about a football game, provide context to clarify:
```sh theme={null}
curl -X POST 'https://api.deepl.com/v2/translate' \
--header 'Authorization: DeepL-Auth-Key [your key]' \
--header 'Content-Type: application/json' \
--data '{
"text": [
"Die Person stand vor dem Tor."
],
"target_lang": "EN-US",
"context": "Es war ein Fußballspiel."
}'
```
**Output with context:**
```text theme={null}
"The person was standing in front of the goal."
```
### When to use this approach
* You're translating short snippets that lack built-in context
* The text contains words with multiple meanings
* The surrounding content makes the intended meaning clear
***
## How to use context for grammatical gender
When grammatical gender isn't clear from the source text, context can provide the necessary clues.
### Example
Without context, DeepL may not use the desired gender when translating:
```sh theme={null}
curl -X POST 'https://api.deepl.com/v2/translate' \
--header 'Authorization: DeepL-Auth-Key [your key]' \
--header 'Content-Type: application/json' \
--data '{
"text": [
"The teacher asked the class to tidy up after they finished the lesson."
],
"target_lang": "DE"
}'
```
**Output without context:** (uses masculine form "Lehrer")
```text theme={null}
"Der Lehrer bat die Klasse, nach der Stunde aufzuräumen."
```
You can provide context from the surrounding text that clarifies the teacher's gender:
```sh theme={null}
curl -X POST 'https://api.deepl.com/v2/translate' \
--header 'Authorization: DeepL-Auth-Key [your key]' \
--header 'Content-Type: application/json' \
--data '{
"text": [
"The teacher asked the class to tidy up after they finished the lesson."
],
"target_lang": "DE",
"context": "She did not want to tidy up herself."
}'
```
**Output with context:** (uses feminine form "Lehrerin")
```text theme={null}
"Die Lehrerin bat die Klasse, nach der Stunde aufzuräumen."
```
### When to use this approach
* Translating into languages with grammatical gender
* The source text doesn't specify gender
* You have surrounding sentences that contain gender clues
***
## How to use context for consistent name translation
When translating names in headlines or short snippets, the same name might be transliterated differently unless additional context is provided.
### Example
As noted in the [`text` field reference](/api-reference/translate/request-translation), each text in the array is translated independently and texts do not share context with each other. This results in different transliterations for the name "Sergej".
```sh theme={null}
curl -X POST 'https://api.deepl.com/v2/translate' \
--header 'Authorization: DeepL-Auth-Key [your key]' \
--header 'Content-Type: application/json' \
--data '{
"text": [
"Sergej gibt Stellungnahme ab",
"Sergej Zhivkov erklärte gestern, dass neue Maßnahmen ergriffen werden."
],
"source_lang": "DE",
"target_lang": "EN-US"
}'
```
**Output without context:**
```json theme={null}
{
"translations": [
{
"detected_source_language": "DE",
"text": "Sergej makes a statement"
},
{
"detected_source_language": "DE",
"text": "Sergei Zhivkov explained yesterday that new measures will be taken."
}
]
}
```
Providing a longer snippet of `context` containing the complete name results in consistent transliteration:
```sh theme={null}
curl -X POST 'https://api.deepl.com/v2/translate' \
--header 'Authorization: DeepL-Auth-Key [your key]' \
--header 'Content-Type: application/json' \
--data '{
"text": [
"Sergej gibt Stellungnahme ab",
"Sergej Zhivkov erklärte gestern, dass neue Maßnahmen ergriffen werden."
],
"source_lang": "DE",
"target_lang": "EN-US",
"context": "Sergej Zhivkov erklärte gestern, dass neue Maßnahmen ergriffen werden."
}'
```
**Output with context:**
```json theme={null}
{
"translations": [
{
"detected_source_language": "DE",
"text": "Sergei issues statement"
},
{
"detected_source_language": "DE",
"text": "Sergei Zhivkov declared yesterday that new measures will be taken."
}
]
}
```
### When to use this approach
* Translating news headlines separately from article bodies
* Transliterating names with multiple possible spellings in your target language
***
## Choosing the right feature
The `context` parameter is one of several ways to influence translation output. For a comparison of `context` with glossaries, style rules, custom instructions, and translation memories, see [Choosing the right feature](/docs/customize/overview#choosing-the-right-feature).
***
## Technical details
### Cost
Characters in the `context` parameter do not count toward billing. Only characters sent in the `text` parameter are billed.
### Size limit
There is no size limit for the `context` parameter itself, but the request body size limit of 128 KiB applies to all text translation requests.
### Multi-`text` requests
As noted in the [`text` field reference](/api-reference/translate/request-translation), each text in the array is translated independently — they do not share context with each other.
In this example, "Tor" might be translated as "gate" instead of "goal" because the first `text` doesn't have access to the second one's content.
```python theme={null}
{
"text": [
"Die Person stand vor dem Tor.",
"Es war ein Fußballspiel."
],
"target_lang": "EN-US"
}
```
To ensure "Tor" is translated as "goal", you can add additional sentences into the `context` parameter, or keep related content together in one `text` parameter.
If you send a request with both `context` and multiple `text` parameters, the `context` parameter will be applied to each one.
### Document translation
When using the [document translation endpoint](/api-reference/document/upload-and-translate-a-document), the engine automatically uses the broader document context. You don't need to provide explicit context for full documents.
### Tag handling
When using `tag_handling=xml` or `tag_handling=html`, tags are *not* used as a context boundary. The translation engine automatically looks across all content provided in each `text` parameter. As with other types of texts, you may need to provide additional `context` when translating single tags without surrounding content.
***
## Next steps
Now that you understand the context parameter:
* **Try it yourself:** Review the [text translation API reference](/api-reference/translate/request-translation) for complete context parameter specifications
* **Enforce terminology:** Learn how to use [glossaries](/docs/customize/managing-glossaries) for consistent translations across all content
* **Control style and tone:** Explore [style rules](/docs/customize/using-style-rules) for formatting and tone instructions
* **Translate full documents:** Understand how [document translation](/api-reference/document/upload-and-translate-a-document) automatically handles context
# How to Use Custom Reporting Tags
Source: https://developers.deepl.com/docs/learning-how-tos/examples-and-guides/how-to-use-custom-reporting-tags
Learn how to attach reporting tags to text translation and Voice API requests so you can break down usage by team, project, or category.
**This guide shows you:**
* When to use custom reporting tags
* How to attach a tag to a text translation or Voice API request via the `X-DeepL-Reporting-Tag` header
* Tag naming guidance and current limitations
***
## What custom reporting tags are for
Custom reporting tags let your organization attribute API usage to a team, project, customer, or any other category you care about. You attach a tag to each request, and you can then retrieve usage broken down by those tags in two ways: programmatically via the [Get custom tag usage analytics](/api-reference/admin-api/get-custom-tag-usage-analytics) endpoint, or as a [custom tag-level CSV export](/docs/admin/retrieving-usage-data#csv-export) from the account UI.
Common use cases:
* **Multi-tenant applications**: attribute translation volume to the end customer making each request
* **Internal cost allocation**: split usage across teams or business units that share a single API key
Only tagged requests appear in custom-tag reports, so tag every request you want to track.
Custom tag data is recorded only for text translation and Voice API requests. Support for additional request types will be added in a future update.
***
## How to tag a translate request
Set the optional `X-DeepL-Reporting-Tag` header on your translate request. You define the tag value; no registration is required before sending it. See [Tag naming guidance](#tag-naming-guidance) and [Limitations](#limitations) for the rules tag values must follow.
```sh theme={null}
curl --request POST 'https://api.deepl.com/v2/translate' \
--header 'Authorization: DeepL-Auth-Key [your key]' \
--header 'X-DeepL-Reporting-Tag: team-billing' \
--header 'Content-Type: application/json' \
--data '{
"text": ["Welcome to your dashboard."],
"target_lang": "DE"
}'
```
**Sample response:**
```json theme={null}
{
"translations": [
{
"detected_source_language": "EN",
"text": "Willkommen in Ihrem Dashboard."
}
]
}
```
The translate response is unchanged by tagging. The tag is recorded server-side and surfaces only in custom-tag usage reports: the analytics endpoint and the CSV export.
***
## How to tag a Voice API session
Set the same header on the [session request](/api-reference/voice/request-session). The tag applies to the whole session, so all audio streamed over the resulting WebSocket connection is attributed to it.
```sh theme={null}
curl --request POST 'https://api.deepl.com/v3/voice/realtime' \
--header 'Authorization: DeepL-Auth-Key [your key]' \
--header 'X-DeepL-Reporting-Tag: team-billing' \
--header 'Content-Type: application/json' \
--data '{
"source_media_content_type": "audio/ogg;codecs=opus",
"source_language_mode": "auto",
"target_languages": ["de"]
}'
```
**Sample response:**
```json theme={null}
{
"streaming_url": "wss://api.deepl.com/v3/voice/realtime/connect",
"token": "VGhpcyBpcyBhIGZha2UgdG9rZW4K",
"session_id": "4f911080-cfe2-41d4-8269-0e6ec15a0354"
}
```
The WebSocket connection itself carries no headers, so the session request is the only place a tag can be set. You cannot change or add a tag once a session is running, and you cannot tag individual audio chunks within a session. To attribute audio to more than one tag, open a separate session per tag.
The tag survives [reconnection](/api-reference/voice/reconnect-session): audio streamed after you exchange a token for a new streaming URL is still attributed to the tag set on the original session request, and you do not resend the header.
***
## Tag naming guidance
A few practices keep your reports clean:
* Tags are caller-defined strings. The API records the value you send after applying the normalization rules below.
* Before storage, the API trims leading and trailing whitespace and lowercases the value, so `" Sample-Tag"` is recorded as `sample-tag` and appears that way in analytics responses.
* After normalization, characters are matched exactly. `team-billing` and `team_billing` report as two separate tags.
* Pick a convention and stick to it. Examples: `team-billing`, `customer-{id}`, `project-{id}`.
***
## Tags and API keys
A custom tag is independent of the API key that sent the request. The same tag value can be used across multiple API keys in your organization, and usage from every key that sends `team-billing` rolls up to a single `team-billing` row in the analytics response.
The custom-tag analytics endpoint groups by tag only. Combined breakdowns such as tag-by-API-key or API-key-by-tag are not supported. For a per-key view of usage, use the [Get usage analytics](/api-reference/admin-api/get-usage-analytics) endpoint instead.
Tags are a reporting feature only. You cannot use them to enforce limits, quotas, or any per-request controls. If you need separate controls across workloads, such as capping staging consumption or disabling translation for a specific environment, use a separate API key for each workload and configure [cost control limits](/docs/best-practices/cost-control) per key.
***
## Limitations
* Tag values are limited to 100 characters. Requests with longer values are rejected with a 400 response.
* Tag values must use ASCII characters only, with no internal whitespace and no control characters. Values like `sample tag`, `sample-tág`, or `sample\ntag` are rejected with a 400 response. Leading and trailing whitespace is trimmed before validation (see [Tag naming guidance](#tag-naming-guidance)).
* Each request accepts one tag. Sending multiple values in the header is not supported.
* For the Voice API, a tag covers a whole session and can only be set on the session request. See [How to tag a Voice API session](#how-to-tag-a-voice-api-session).
* Untagged requests are not included in custom-tag analytics. They still count toward your overall usage, which you can retrieve through the [Get usage analytics](/api-reference/admin-api/get-usage-analytics) endpoint.
***
## Next steps
* **Use the client libraries:** See [Sending Custom Reporting Tags with Client Libraries](/docs/learning-how-tos/cookbook/sending-custom-reporting-tags-from-client-libraries) for code snippets in Python, Node.js, Java, .NET, and PHP.
* **Query your tagged usage:** See the [Get custom tag usage analytics](/api-reference/admin-api/get-custom-tag-usage-analytics) reference for date ranges, aggregation options, and pagination, or download a [custom tag-level CSV export](/docs/admin/retrieving-usage-data#csv-export) from the account UI.
* **Get the bigger picture:** Read the [usage data guide](/docs/admin/retrieving-usage-data) to understand how custom-tag reporting fits alongside the other usage data sources.
* **Explore other optional translate parameters:** Learn [how to use the context parameter](/docs/learning-how-tos/examples-and-guides/how-to-use-context-parameter) to improve translation accuracy for ambiguous content.
# Mustache placeholder tags
Source: https://developers.deepl.com/docs/learning-how-tos/examples-and-guides/placeholder-tags
An example for working with placeholder tags—in this case, Mustache tags.
Mustache is a template system that provides “logic-less templates”. [From the Mustache manual](https://mustache.github.io/mustache.5.html):
*Mustache can be used for HTML, config files, source code - anything. It works by expanding tags in a template using values provided in a hash or object.*
Using the DeepL API to translate text that includes Mustache tags can present a challenge. In most if not all cases, users would *not* want to translate the tag key inside a Mustache tag, as the tag key is used to reference values in a hash or object. However, the DeepL API does not recognize Mustache tags and does not have a built-in parameter that can be used to exclude them from translation.
It is possible, however, to pre- and post-process text containing Mustache tags in order to preserve the Mustache tag key during translation. In the Python client library, [we include an example](https://github.com/DeepL/deepl-python/tree/main/examples/mustache) showing how this can be done.
A similar approach could be used for other types of placeholder tags that are not recognized by the DeepL API when users do not want to translate the content inside the tags.
Below is a summary of the Mustache example, and for more detail, you can refer to the [example's README](https://github.com/DeepL/deepl-python/blob/main/examples/mustache/README.md).
* The input Mustache template is parsed to separate the literal text from the Mustache tags.
* The template is modified to replace all Mustache tags with placeholder XML tags. Unique IDs are attached to each placeholder tag to identify them in the translated XML.
* The XML template is translated using DeepL API with XML tag handling activated.
* The translated XML is parsed to identify placeholder tags and replace them with the original Mustache tags.
Please note when using Mustache or other placeholder tags, the translation engine would not know the context or meaning of a tag (i.e. the engine would not know if a tag will populate a name, or an address, or a color, or a numerical value, etc). This lack of context might affect the quality of the translation output.
# How to Translate Between Language Variants
Source: https://developers.deepl.com/docs/learning-how-tos/examples-and-guides/translating-between-variants
Learn how to translate between language variants, like British English and US English, using the DeepL API.
**This guide shows you:**
* How to translate between language variants (e.g., `en-US` to `en-GB`, `pt-PT` to `pt-BR`)
* Which method to choose: Write API, style rules, or custom instructions
* An example workflow for converting American English to British English
***
## Methods for translating between variants
You can use the DeepL API to translate between variants of the same language using 3 methods:
### 1. DeepL Write API
Use the [/write/rephrase](/docs/translate/write-quickstart) endpoint to rephrase text into the target language variant.
**When to use this:**
* You're translating shorter texts (headlines, product names, brief descriptions)
* You want high-quality rephrasing alongside variant translation
```bash Example cURL request theme={null}
curl -X POST 'https://api.deepl.com/v2/write/rephrase' \
--header 'Authorization: DeepL-Auth-Key [yourAuthKey]' \
--header 'Content-Type: application/json' \
--data '{
"text": ["Check out the new fall colors!"],
"target_lang": "en-GB"
}'
```
```json Example response theme={null}
{
"improvements": [
{
"text": "Check out the new autumn colours!",
"detected_source_language": "en",
"target_language": "en-GB"
}
]
}
```
For longer texts, the Write API may rephrase and enhance content beyond simple variant conversion. If you need to maintain the exact structure while only updating locale-specific spelling and grammar, use another method.
Please note that currently, the methods outlined below are not fully supported for this use case and may not always perform as intended. We encourage you to conduct your own evaluations.
### 2. Style rules with custom instructions
Create a reusable [style rule list](/docs/customize/using-style-rules) with attached `custom_instructions` describing the desired variant translation.
**When to use this:**
* You need to maintain the text's content between variants as precisely as possible
* You need consistent variant transformations across many translation requests
* You want to reuse the same variant rules without repeating the custom instructions
```bash Example cURL request theme={null}
curl -X POST 'https://api.deepl.com/v2/translate' \
--header 'Authorization: DeepL-Auth-Key [yourAuthKey]' \
--header 'Content-Type: application/json' \
--data '{
"text": ["I went to the pharmacy."],
"target_lang": "en-GB",
"style_id": "your-style-rule-id"
}'
```
```json Example response theme={null}
{
"translations": [
{
"detected_source_language": "EN",
"text": "I went to the chemist's."
}
]
}
```
Glossaries and style rules are unique to each of DeepL's global data centers and are not shared between them.
Clients using the `api-us.deepl.com` endpoint will not be able to access glossaries or style rules created in the UI at this time.
### 3. Per-request custom instructions
Add [custom\_instructions](/api-reference/translate/request-translation#body-custom-instructions) describing the desired variant translation directly into your `/translate` requests.
**When to use this:**
* You need to maintain the text's content between variants as precisely as possible
* You need ad-hoc, one-off translations with specific variant requirements
* You don't want to manage separate style rules
```bash Example cURL request theme={null}
curl -X POST 'https://api.deepl.com/v2/translate' \
--header 'Authorization: DeepL-Auth-Key [yourAuthKey]' \
--header 'Content-Type: application/json' \
--data '{
"text": ["I went to the pharmacy."],
"target_lang": "en-GB",
"custom_instructions": ["translate to British English"]
}'
```
```json Example response theme={null}
{
"translations": [
{
"detected_source_language": "EN",
"text": "I went to the chemist's."
}
]
}
```
You can specify up to 10 custom instructions per request, each with a maximum of 300 characters.
***
## Next steps
Now that you understand how to translate between language variants:
* **Try it yourself:** Test out style rules and custom instructions in the [text translation API playground](/api-reference/translate/request-translation?playground=open)
* **Learn about the Write API:** Explore the [/write/rephrase endpoint](/docs/translate/write-quickstart) for high-quality variant translation and rephrasing
* **Manage reusable rules:** Learn how to create [style rules](/docs/customize/using-style-rules) for systematic variant transformations
* **Improve translation quality:** Understand how [the context parameter](/docs/learning-how-tos/examples-and-guides/how-to-use-context-parameter) can enhance ambiguous translations
# Alpha and beta features
Source: https://developers.deepl.com/docs/resources/alpha-and-beta-features
Learn more about alpha and beta features in the DeepL API and how to best make use of them.
You might see API features in the documentation that are labeled as "alpha" or "beta". You can find the official definition in our terms and conditions ([section 3.1.5](https://www.deepl.com/en/pro-license)):
> 3.1.5 DeepL is free to provide customers with additional functions in alpha or beta versions on a test basis ("Test Functions"). These Test Functions are marked as such or as alpha or beta. Test Functions are not the subject of this Agreement. DeepL may make them available voluntarily to all or individual customers and the Customer is not obliged to make any payment for the use of Test Functions. Test Functions are intended for test use by the Customer and evaluation by DeepL. They are not final products or features and may contain bugs or other inaccuracies. DeepL can change, adapt or discontinue the Test Functions at any time.
In summary, features labeled as alpha or beta:
* Are not intended to be used in production
* Could be deprecated by DeepL at any time and without advance notice
* Could be changed in a way that breaks API clients relying on them at any time and without advance notice
Please keep this in mind when exploring or testing features labeled as alpha or beta.
# API Status Page
Source: https://developers.deepl.com/docs/resources/api-status-page
Monitor the operational status and availability of the DeepL API
The [DeepL Status Page](https://status.deepl.com) provides a real-time, [API-specific view](https://status.deepl.com/?tab=api) of the operational status of the DeepL API. The same page also covers DeepL's other services, along with incident reports and upcoming maintenance.
## What you can check
* **Current status** of all API services across all supported regions
* **90-day availability** history per service group (Translation, Write, Document Translation, Glossaries, and more)
* **Active and past incidents** affecting the API
* **Pro and Free API tier** status separately
## When to check
* If you're receiving HTTP 5xx errors from the API
* If you're experiencing elevated latency or timeouts
* Before investigating issues in your own integration — confirm the API is operational first
Go to the API Status Page
For the status of all DeepL services (including web, desktop apps, and non-API products), see [status.deepl.com](https://status.deepl.com).
# Breaking changes (Change Notices)
Source: https://developers.deepl.com/docs/resources/breaking-changes-change-notices
In this section, we'll outline **planned deprecations and breaking changes** that might affect applications using the DeepL API.
To learn about new releases, please see the [Release notes](/docs/resources/release-notes) page.
# July 2024: Deprecation of insecure cipher suites
Source: https://developers.deepl.com/docs/resources/breaking-changes-change-notices/july-2024-deprecation-of-insecure-cipher-suites
## Change Notice
On or after July 29, 2024, DeepL will deprecate support for insecure cipher suites. Affected customers will need to upgrade their TLS library so that it doesn't use a cipher suite that we’ll be deprecating.
If you are using an insecure cipher suite and do not make this update, you’ll no longer be able to use the DeepL API from the deprecation date onward.
Update on August 8, 2024: The deadline for this cipher deprecation has been extended to Monday, September 2, 2024.
Please be sure to update your applications before September 2. We will not be able to extend the deadline any further.
### Which cipher suites are being deprecated for the DeepL API?
On or after July 29, 2024, we will be deprecating the following three cipher suites:
* TLS\_ECDHE\_RSA\_WITH\_AES\_256\_CBC\_SHA384 (0xc028)
* TLS\_ECDHE\_RSA\_WITH\_AES\_256\_CBC\_SHA (0xc014)
* TLS\_ECDHE\_RSA\_WITH\_AES\_128\_CBC\_SHA (0xc013)
This means that any application with a TLS library:
* That uses one of these cipher suites
* *And* does not support any of the cipher suites that will continue to be supported by DeepL
...will no longer be able to connect to the DeepL API.
### Why is DeepL doing this now?
The cipher suites that we’re deprecating have a historical track record of security weaknesses. They're still vulnerable to attacks that may enable a bad actor to decrypt data. We consider this to be an unacceptable security risk, especially given our commitment to keeping our customers’ data secure.
After deprecating the ciphers listed above, the DeepL API will accept the same set of cipher suites supported by our web translator ([deepl.com](https://www.deepl.com)) today.
### What happens if a user continues to use a deprecated cipher suite?
If you continue to use one of the cipher suites we’re deprecating, you won’t be able to access the DeepL API. This means that, for example, CAT tool plugins would no longer work properly.
### What cipher suites will be supported after deprecation?
We will continue to support the following cipher suites after deprecation:
* TLS 1.3 (suites in server-preferred order)
* TLS\_AES\_256\_GCM\_SHA384 (0x1302)
* TLS\_CHACHA20\_POLY1305\_SHA256 (0x1303)
* TLS\_AES\_128\_GCM\_SHA256 (0x1301)
* TLS 1.2 (suites in server-preferred order)
* TLS\_ECDHE\_RSA\_WITH\_AES\_256\_GCM\_SHA384
* TLS\_ECDHE\_RSA\_WITH\_AES\_128\_GCM\_SHA256
### What action should I take so that I’m not affected?
**If you’re a developer of your own application with the DeepL API:**
* Ensure the TLS library you’re using supports one of the ciphers listed above
If you’re using a third-party plugin that is powered by the DeepL API:
* Update to the most recent version of the plugin Ask the plugin provider to upgrade their TLS library so that one of the cipher suites listed above is supported
### How can I test my application after making changes to ensure I’m using a supported cipher suite?
September 2024 update: because the cipher deprecation has been carried out according to schedule, the endpoint below is obsolete and is no longer available for use.
We created a test endpoint at `api-test-tls.deepl.com` that only supports the cipher suites that will still be available after the deprecation of insecure suites.
You can send a test request to this endpoint to be sure that you’re using a supported cipher suite. If you receive a translation response back from the DeepL API, then you should not be affected by the deprecation.
Below is an example cURL request using the test endpoint that Pro API users can use. Please remember to replace the `[yourAuthKey]` placeholder with your API key.
```bash theme={null}
curl -X POST 'https://api-test-tls.deepl.com/v2/translate' \
--header "Authorization: DeepL-Auth-Key [yourAuthKey]" \
--header "Content-Type: application/json" \
--data \
'{
"target_lang": "DE",
"text" : ["Hello, world!"]
}'
```
# March 2025: Deprecating GET requests to /translate and authenticating with auth_key
Source: https://developers.deepl.com/docs/resources/breaking-changes-change-notices/march-2025-deprecating-get-requests-to-translate-and-authenticating-with-auth_key
**Important:** The deprecations described on this page apply to both the `v1` (CAT tool) and `v2` API versions.
## Change Notice
On or after March 14, 2025, DeepL will deprecate two little-used API features.
* **You will no longer be able to send GET requests or query parameters to the**`/translate`**endpoint.** Going forward, `/translate` will accept only POST requests with data included in the request body.
* **You will no longer be able to authenticate a request to any endpoint by sending an API key in a query parameter.** Instead, send your API key in an HTTP header named `Authorization` .
* **If you use one of DeepL's officially-supported client libraries, you won't be negatively affected by the breaking changes and do not need to update your application.** Specifically, we've confirmed the following client library versions:
* [deepl-php](https://github.com/deeplcom/deepl-php): 1.0.0+
* [deepl-python](https://github.com/deeplcom/deepl-python): 1.0.0+
* [deepl-java](https://github.com/DeepL/deepl-java): 1.0.0+
* [deepl-dotnet](https://github.com/DeepL/deepl-dotnet): 1.0.0+
* [deepl-node](https://github.com/DeepL/deepl-node): 1.1.0+
* [deepl-rb](https://github.com/DeepL/deepl-rb): 3.0.0+ (the first version where DeepL took over ownership of the Ruby client library)
### Use POST for /translate
Going forward, you will need to send requests to the `/translate` endpoint using POST, not GET. This also means you will not be able to send such requests using only a URL. You will need to send data in the request body, not in query parameters.
[This example from the documentation](/api-reference/translate/request-translation) shows an HTTP POST request to translate the English sentence "Hello, world!" into German.
```HTTP theme={null}
POST /v2/translate HTTP/2
Host: api.deepl.com
Authorization: DeepL-Auth-Key [yourAuthKey]
User-Agent: YourApp/1.2.3
Content-Length: 45
Content-Type: application/json
{"text":["Hello, world!"],"target_lang":"DE"}
```
Going forward, the API will reject a GET request like this.
```HTTP theme={null}
GET /v2/translate?text=Hello%2C%20world!&target_lang=DE HTTP/2
Host: api.deepl.com
Authorization: DeepL-Auth-Key [yourAuthKey]
User-Agent: YourApp/1.2.3
```
Similarly, the API will reject a request made with a URL and query string.
```
https://api.deepl.com/v2/translate?auth_key=yourAuthKey&text=Hello%2C%20world!&target_lang=DE
```
### Authenticate with an HTTP header
Going forward, you will need to authorize any API request, to any endpoint, by including your API key in an HTTP header named `Authorization`, like this:
```HTTP theme={null}
Authorization: DeepL-Auth-Key [yourAuthKey]
```
See the example above. For detailed information on authorization and how to use the `Authorization` header, please see [the documentation](https://developers.deepl.com/docs/getting-started/auth).
Going forward, you will not be able to authorize any request, to any endpoint, by including your API key in an `auth_key` query parameter.
### Why is DeepL doing this now?
#### Use POST for /translate
It is a standard API practice to use GET to request data and POST to send data. To request a translation, one must send data, making POST the customary choice. By disallowing GET and query params for this endpoint, we bring our API in line with industry best practices.
Additionally, data sent in query parameters is less secure than data sent in a POST body, as URLs with query parameters may be stored in browser history, discovered in logs, and more. This change helps us make the API more efficient and more private for everyone.
#### Authenticate with an HTTP header
Similarly, the API best practice is to authenticate a request by including the API key in an HTTP header. Sending an API key in a GET request or in a URL risks exposing the API key to the public via logs or other means. For privacy and security reasons, we must disallow this practice.
### What if I continue to use a deprecated feature?
After the date above, translation requests that use GET or query parameters will fail. Requests to any API endpoint that attempt to authenticate with the old `auth_key` parameter will fail as well.
### What action should I take so that I’m not affected?
If you develop your own application using the DeepL API:
* Ensure your code is not using either of the deprecated features.
If you’re using a third-party plugin that is powered by the DeepL API:
* Update to the most recent version of the plugin. If you experience trouble, contact the plugin provider to make sure they are aware of these deprecations.
For further questions, please visit [our support page](https://support.deepl.com).
# November 2025: Deprecation of query parameter and request body authentication
Source: https://developers.deepl.com/docs/resources/breaking-changes-change-notices/november-2025-deprecation-of-legacy-auth-methods
## Change Notice
Update: The deadline for this deprecation has been extended to January 15th, 2026.
On November 1st, 2025, DeepL will fully obsolesce support for providing authentication information in an `auth_key` query parameter or in an `auth_key` field of the request body.
Deprecation of this feature was previously announced in [March 2025](/docs/resources/breaking-changes-change-notices/march-2025-deprecating-get-requests-to-translate-and-authenticating-with-auth_key), but we have continued to support it temporarily to allow more time for customers to complete the migration.
You will no longer be able to authenticate a request to any endpoint by sending an API key in a query parameter or request body. Instead, send your API key in an HTTP header named `Authorization`.
* **You will no longer be able to authenticate a request to any endpoint by sending an API key in a query parameter or in the request body.** Instead, send your API key in an HTTP header named `Authorization` .
* **If you use one of DeepL's officially-supported client libraries, you won't be negatively affected by the breaking changes and do not need to update your application.** Specifically, we've confirmed the following client library versions:
* [deepl-php](https://github.com/deeplcom/deepl-php): 1.0.0+
* [deepl-python](https://github.com/deeplcom/deepl-python): 1.0.0+
* [deepl-java](https://github.com/DeepL/deepl-java): 1.0.0+
* [deepl-dotnet](https://github.com/DeepL/deepl-dotnet): 1.0.0+
* [deepl-node](https://github.com/DeepL/deepl-node): 1.1.0+
* [deepl-rb](https://github.com/DeepL/deepl-rb): 3.0.0+
Going forward, you will need to authorize any API request, to any endpoint, by including your
API key in an HTTP header named `Authorization`, like this:
```
Authorization: DeepL-Auth-Key [yourAuthKey]
```
For detailed information on authorization and how to use the Authorization header, please see the [documentation](https://developers.deepl.com/docs/getting-started/auth).
Going forward, you will not be able to authorize any request, to any endpoint, by including your API key in an `auth_key` query parameter or an `auth_key` field of the request body.
### Why is DeepL doing this now?
The API best practice is to authenticate a request by including the API key in an HTTP header. Sending an API key in a GET request or in a URL risks exposing the API key to the public via logs or other means. For privacy and security reasons, we must disallow this practice.
### What if I continue to use non-header authentication?
After the date above, translation requests that attempt to authenticate with an `auth_key` query parameter or an `auth_key` field in the request body will fail with a `403 Forbidden` response.
### What action should I take so that I’m not affected?
If you develop your own application using the DeepL API:
* Ensure your code is not using either of the deprecated features.
If you’re using a third-party plugin that is powered by the DeepL API:
* Update to the most recent version of the plugin. If you experience trouble, contact the plugin provider to make sure they are aware of these deprecations.
For further questions, please visit [our support page](https://support.deepl.com).
# Developer Community
Source: https://developers.deepl.com/docs/resources/deepl-developer-community
The official DeepL Developer Community is on [Discord](https://discord.gg/deepl) and open for everyone to join. It's a friendly, collaborative space to discuss everything DeepL, development, AI, tech and more!
The community is a place to:
* Introduce yourself and connect with other DeepL community members
* Share what you're currently working on and how you're integrating DeepL into your software projects
* Join upcoming live events and webinars exclusive to the community
* Ask for coding help and tips on development best practice, as well as offering your own help and experience
[Join the Discord community here!](https://discord.gg/deepl)
# Language release process
Source: https://developers.deepl.com/docs/resources/language-release-process
Here's what API users can expect when DeepL adds translation support for a new language or language variant.
On a regular basis, DeepL adds translation support for new languages or language variants. In this article,
we describe the process we'll follow with a new language or variant release.
## Language codes follow BCP 47
DeepL language codes follow [BCP 47](https://www.rfc-editor.org/rfc/rfc5646). A language code always
includes a base language subtag (e.g. `en`, `zh`), and may include additional subtags for script, region,
or variant where needed to distinguish variants. For example:
* `EN-US`, `PT-BR` -- region subtag to distinguish regional variants.
* `ZH-HANS`, `ZH-HANT` -- script subtag to distinguish writing systems.
BCP 47 is an expansive standard, and language codes can vary significantly in structure and length. As DeepL
adds support for more languages and variants, new codes may use any combination of subtags permitted by the
spec. For example, codes like `sr-Cyrl-RS` or `sr-Latn-RS` (Serbian in Cyrillic vs. Latin script, as used in
Serbia) are valid BCP 47 codes -- while DeepL does not support these today, your integration should be able
to handle codes of this form if they are added in the future.
**Do not hardcode assumptions about the format of language codes.** For example, do not assume that language
codes will always be exactly two letters, or that a hyphenated code will always be in the format `xx-YY`.
Instead, always treat the `lang` codes returned by the [/languages endpoint](/api-reference/languages/retrieve-supported-languages) as
opaque identifiers. If you need to parse language codes, use a BCP 47-compliant library rather than writing
custom parsing logic -- the full spec includes subtags for script, region, variant, extensions, and private
use, and partial implementations are a common source of bugs.
## What happens when a new language is released
### Language release process for v3/languages
The [`/v3/languages`](/api-reference/languages/retrieve-supported-languages) endpoint provides flexibility
to specify which languages are supported by different products and which features are supported by each
language. Languages are added individually to each API resource, and new languages may initially be flagged
as beta before they are stable.
### Language release process for v2/languages
The `/v2/languages` endpoint is deprecated, and may not be extended with all new languages we support.
You should build your integration to use `/v3/languages` instead.
* We will add the language code for the newly supported language or variant to the list on the
[Supported languages](/docs/getting-started/supported-languages) page in the API documentation. The list
shows support for text and document translation.
* If a newly added language or variant supports both text and document translation, we will add the language
or variant to the [`/v2/languages`](/api-reference/languages/retrieve-supported-languages) endpoint response. The variant code used
depends on the characteristics of the variant:
* In some cases, a variant is primarily used in a specific region, and so a region subtag is the best way
to identify it (e.g. `EN-US`, `PT-BR`).
* In other cases, a variant is used widely across multiple regions, and so a script subtag is more
appropriate (e.g. `ZH-HANS`, `ZH-HANT`). The subtag structure will be selected by DeepL on a case-by-case
basis following BCP 47 conventions.
* In cases where a new language code with a variant duplicates the behavior of an existing language code
without a variant (e.g. `ZH-HANS` was recently added as a language code for translating into simplified
Chinese, along with `ZH`):
* In the [`/v2/languages`](/api-reference/languages/retrieve-supported-languages) endpoint response, we will continue to return both
language codes in two separate dicts with the same value in the `"name"` field.
* For backwards compatibility, we will continue to support the original language code (in this example,
`ZH`) for text and document translation.
* We will add the language code for the newly supported language or variant to our
[OpenAPI spec](https://github.com/DeepL/openapi/).
# OpenAPI spec
Source: https://developers.deepl.com/docs/resources/open-api-spec
Download the OpenAPI specification for the DeepL API
This repository contains an [OpenAPI specification](https://openapis.org/) of the DeepL API in YAML and JSON formats.
| File | Format | Description |
| ---------------------------------------------------------------------------------------------------------------------- | ------ | ----------------------------------------- |
| [`openapi.yaml`](https://raw.githubusercontent.com/DeepL/api-docs/main/api-reference/openapi.yaml) | YAML | Main REST API spec (source of truth) |
| [`openapi.json`](https://raw.githubusercontent.com/DeepL/api-docs/main/api-reference/openapi.json) | JSON | Same content, auto-generated from YAML |
| [`voice.asyncapi.yaml`](https://raw.githubusercontent.com/DeepL/api-docs/main/api-reference/voice/voice.asyncapi.yaml) | YAML | AsyncAPI spec for the streaming Voice API |
| [`voice.asyncapi.json`](https://raw.githubusercontent.com/DeepL/api-docs/main/api-reference/voice/voice.asyncapi.json) | JSON | Same content, auto-generated from YAML |
You can use these specs to explore the API in tools like [Postman](https://www.postman.com/), or to auto-generate SDKs and code libraries using tools such as [Swagger Editor](https://editor.swagger.io/?url=https://raw.githubusercontent.com/DeepL/api-docs/main/api-reference/openapi.yaml) or [OpenAPI Generator](https://openapi-generator.tech/).
Swagger's "Try it out" in-browser simulator creates valid curl requests, but requests will fail due to [CORS restrictions](/docs/best-practices/cors-requests).
The spec files live in the [`api-reference/`](https://github.com/DeepL/api-docs/tree/main/api-reference) directory of the [api-docs repository](https://github.com/DeepL/api-docs). If you encounter issues or have feature requests, [create an issue](https://github.com/DeepL/api-docs/issues).
# Changelog
Source: https://developers.deepl.com/docs/resources/roadmap-and-release-notes
The latest features and improvements in the DeepL API, plus what's coming next
* Editing the contents of an existing [translation memory](/docs/customize/using-translation-memories) via API
* Usage reporting by language pair
## September 1 - Croatian and Tagalog Translated Speech in the Voice API
* The [Voice API](/docs/voice/overview) now generates translated speech (voice output) for `hr` (Croatian) and `tl` (Tagalog), in beta. Transcription and translation for these languages were already supported; this adds synthesized audio.
* Translated speech for both languages is provided by external service partners. In the [`GET /v3/languages?resource=voice`](/docs/languages/using-the-languages-api) response, `translated_speech` is reported with `"external": true` and beta status — call with `include=beta&include=external` to see it.
* See the [supported languages table](/docs/voice/supported-voice-languages) for the full list.
## August 18 - Custom Reporting Tags for the Voice API
* [Custom reporting tags](/docs/learning-how-tos/examples-and-guides/how-to-use-custom-reporting-tags), which attribute API usage to a team, project, or other category via the `X-DeepL-Reporting-Tag` request header, now work for the [Voice API](/docs/voice/overview) in addition to text translation. Send the header on the [session request](/api-reference/voice/request-session) and the whole session's usage is attributed to that tag.
* The [custom tag usage analytics endpoint](/api-reference/admin-api/get-custom-tag-usage-analytics) reports voice usage as `speech_to_text_minutes` and `speech_to_speech_minutes` alongside the existing character fields. Voice minutes are not included in `total_characters`.
* A tag can only be set on the session request, since the WebSocket connection carries no headers. To attribute audio to more than one tag, open a separate session per tag.
## August 11 - Translation Memory Management API
* You can now create, inspect, export, and delete [translation memories](/docs/customize/using-translation-memories) through the API. Translation memories store previously translated segments so the same source text produces consistent output across projects. Previously, the API could only list the translation memories on your account, and everything else had to be done in the DeepL UI.
* [`POST /v3/translation_memories/import`](/api-reference/translation-memory/import-a-translation-memory) creates a translation memory from a TMX file. The request declares the file and returns a signed upload URL plus a `job_id`; you upload the file to that URL and poll the job for the new `translation_memory_id`.
* [`POST /v3/translation_memories/{translation_memory_id}/export`](/api-reference/translation-memory/export-a-translation-memory) exports a translation memory as TMX, also as a background job.
* [`GET /v3/translation_memories/jobs/{job_id}`](/api-reference/translation-memory/retrieve-a-translation-memory-job) reports the status of both import and export jobs.
* [`GET /v3/translation_memories/{translation_memory_id}`](/api-reference/translation-memory/retrieve-a-translation-memory) retrieves a single translation memory, and [`GET /v3/translation_memories/{translation_memory_id}/segments`](/api-reference/translation-memory/list-translation-memory-segments) pages through its stored segments with cursor-based pagination.
* [`DELETE /v3/translation_memories/{translation_memory_id}`](/api-reference/translation-memory/delete-a-translation-memory) permanently deletes a translation memory and all of its segments.
* Reading requires an API key with the `translation_memories:read` scope; importing and deleting require `translation_memories:write`. See [permission scopes](/docs/admin/permission-scopes).
* Editing the contents of an existing translation memory is not yet supported. To change what a translation memory contains, import a new one.
## August 5 - Custom Tag-Level CSV Export
* Usage broken down by [custom reporting tag](/docs/learning-how-tos/examples-and-guides/how-to-use-custom-reporting-tags) can now be exported as a CSV report from the account UI, in addition to being available through the [custom tag usage analytics endpoint](/api-reference/admin-api/get-custom-tag-usage-analytics) in the Admin API. Custom tags attribute API usage to a team, project, or other category via the `X-DeepL-Reporting-Tag` request header.
* The "Download CSV usage report" button on the [API Keys & Limits tab](https://www.deepl.com/your-account/keys) and the [API Usage tab](https://www.deepl.com/your-account/usage) is now a dropdown: select "Custom tag-level report" or "API key-level report".
* See the [CSV export section of the usage data guide](/docs/admin/retrieving-usage-data#csv-export) for supported time ranges and report contents.
## July 22 - Admin API Available to All API Pro Subscribers
* The [Admin API](/docs/admin/overview#the-admin-api), which lets admins [manage developer keys](/api-reference/admin-api/managing-developer-keys/create-key) programmatically and retrieve [usage analytics](/docs/admin/retrieving-usage-data#admin-api-analytics) grouped by API key or custom tag, is now available to all API Pro subscribers, in addition to API Growth and API Enterprise subscribers.
* Previously, API Pro subscribers had to request access through their customer success manager or DeepL support. No action is required to enable access.
* Create an admin key in the ["Admin Keys" tab](https://www.deepl.com/your-account/admin) of your account to get started. See [Managing API Keys in the Account UI](/docs/admin/managing-api-keys#manage-admin-api-keys) for instructions, or jump into the [Admin API Quickstart](/docs/admin/quickstart).
## July 17 - German (Swiss) and French (Canadian) Generally Available
* `de-CH` (German, Swiss) and `fr-CA` (French, Canadian) target language variants have moved out of beta and are now generally available for text and document translation.
* Characters translated into these languages are now billed and count against your character threshold, like other supported languages. During the beta phase, they were not billed.
* See the [supported languages table](/docs/getting-started/supported-languages) for the full list of variants and their supported features.
## July 17 - Voice API Speech Output Generally Available
* **Speech-to-speech** (translated speech output) moves out of closed beta and is now **generally available**.
* See [understanding voice sessions](/docs/voice/understanding-voice-sessions#translated-speech) for how translated speech works and the [supported languages table](/docs/voice/supported-voice-languages) for language availability.
## July 16 - Combined Status Page
* DeepL now publishes a single [status page](https://status.deepl.com) covering all services, with a dedicated [API view](https://status.deepl.com/?tab=api) for real-time API status and incidents.
## July 14 - Watermarking for Document Translation
* [`POST /v2/document`](/api-reference/document/upload-and-translate-a-document) now accepts an `enable_watermark` parameter. When set to `true`, a "Translated by DeepL" watermark is applied to the translated document.
* Supported for `docx` and `pdf` output only; the parameter is ignored for all other formats.
* See the [document translation](/api-reference/document#request-body-descriptions) overview page for parameter details.
## July 7 - New Document Formats and Higher File Size Limits
* [`POST /v2/document`](/api-reference/document/upload-and-translate-a-document) now supports five additional file formats: `idml` (Adobe InDesign), `xml`, `json`, `dita` (DITA topics), and `mif` (Adobe FrameMaker).
* XLIFF support has been expanded to versions 1.2, 2.0, and 2.1 (previously 2.1 only).
* File size limits on DeepL API Pro have increased to 100 MB for Word (`.docx`), PowerPoint (`.pptx`), and PDF (`.pdf`). See [Usage and limits](/docs/resources/usage-limits) for the full per-format table.
* See [document translation best practices](/docs/best-practices/document-translations) for format-specific guidance and known constraints.
## July 2 - New Voice API Languages: Hindi, Malay, and Tamil
* The Voice API now supports `hi` (Hindi), `ms` (Malay), and `ta` (Tamil) in beta. Translation is provided by DeepL; transcription and translated speech are provided by external service partners.
* These languages are marked `"external": true` in the [`GET /v3/languages?resource=voice`](/docs/languages/using-the-languages-api) response. Because they are beta and external, call with `include=beta&include=external` to see them.
* See the [supported languages table](/docs/voice/supported-voice-languages) for the full list.
## June 29 - Spoken Terms API Management
* New [`/v3/spoken-terms`](/docs/customize/improving-transcription-with-spoken-terms) endpoints provide full API management for Spoken Terms collections, which improve transcription accuracy for company-specific terminology, acronyms, and proper names in the Voice API.
* Create, list, retrieve, update, and delete Spoken Terms collections programmatically. Each collection contains one or more term lists, with each term list holding terms for a single language.
* Key endpoints:
* [`POST /v3/spoken-terms`](/api-reference/spoken-terms/create-spoken-terms-collection) - Create a new collection with term lists
* [`GET /v3/spoken-terms`](/api-reference/spoken-terms/list-all-spoken-terms) - List all collections
* [`GET /v3/spoken-terms/{spoken_terms_id}/entries`](/api-reference/spoken-terms/retrieve-spoken-terms-entries) - Retrieve terms for a specific language
* [`PATCH /v3/spoken-terms/{spoken_terms_id}`](/api-reference/spoken-terms/edit-spoken-terms-details) - Update collection name or merge new terms
* [`PUT /v3/spoken-terms/{spoken_terms_id}/term-lists`](/api-reference/spoken-terms/replace-or-create-term-list) - Replace or create a term list for a language
* [`DELETE /v3/spoken-terms/{spoken_terms_id}`](/api-reference/spoken-terms/delete-spoken-terms) - Delete a collection
* This completes the API management capabilities announced in the May 19 Voice API Spoken Terms release. Previously, Spoken Terms could only be managed through [DeepL Home](https://www.deepl.com/en/voice/spoken-terms).
## June 24 - API Key Permissions General Availability
* [Understanding API Key Permissions](/docs/admin/api-key-permissions) are now generally available. Scope a developer API key to specific endpoints so it can perform only the operations you allow.
* Available on the API Pro, API Developer, API Growth, and API Enterprise plans.
* Assign scopes when [creating or editing a key](https://www.deepl.com/your-account/keys). A scoped key returns `403 Forbidden` on any endpoint its scopes don't cover.
## June 23 - Multiple Glossaries per Translation Request
* [`POST /v2/translate`](/api-reference/translate/request-translation) and [`POST /v2/document`](/api-reference/document/upload-and-translate-a-document) now accept a `glossary_ids` parameter, allowing you to apply up to 5 glossaries to a single translation request.
* Useful when terminology is split across multiple glossaries (for example, a shared brand glossary plus a project-specific glossary) that you want applied together without merging them.
* `glossary_ids` requires `source_lang` and is mutually exclusive with the existing `glossary_id` parameter. Every listed glossary must contain a dictionary for the requested language pair.
* See the [text translation](/api-reference/translate/request-translation) and [document translation](/api-reference/document/upload-and-translate-a-document) overview pages for parameter details.
## June 17 - `latency_optimized` Now Supported for All Features
* The `latency_optimized` model type is now fully compatible with all Translate API features, including:
* [Tag handling v2](/docs/translate/translating-xml) (`tag_handling_version=v2`)
* [Style rules](/docs/customize/using-style-rules) (`style_id`)
* [Custom instructions](/docs/customize/custom-instructions) (`custom_instructions`)
* [Translation memories](/docs/customize/using-translation-memories) (`translation_memory_id`)
* All language pairs, including languages previously restricted to `quality_optimized`
* Previously, combining `model_type=latency_optimized` with these features would return an error. These restrictions have been removed.
## June 15 - API Key Permissions (Private Beta)
* Developer API keys can now be scoped to specific endpoints, so a key can be limited to, for example, translating text or reading glossaries. See [Understanding API Key Permissions](/docs/admin/api-key-permissions).
* Assign one or more scopes when [creating or editing a key](https://www.deepl.com/your-account/keys). A scoped key returns `403 Forbidden` on any endpoint its scopes don't cover.
* Currently in private beta for select customers. To request access, contact your customer success manager or [DeepL support](https://support.deepl.com/hc/en-us/requests/new).
* Not yet supported for the Voice API; Voice scopes will follow in a future update.
## June 8 - Style Rules and Translation Memories for Document Translation
* [`POST /v2/document`](/api-reference/document/upload-and-translate-a-document) now accepts `style_id`, `translation_memory_id`, and `translation_memory_threshold`, bringing document translation in line with the parameters already available on text translation.
* `style_id` applies a configured [style rule list](/docs/customize/using-style-rules) to the document translation.
* `translation_memory_id` and `translation_memory_threshold` work as on text translation: pass a translation memory ID to apply stored translations, and set a threshold (0-100) to control how closely source text must match a stored segment. See the [translation memories guide](/docs/customize/using-translation-memories).
## June 5 - Voice API Spoken Terms on External-Provider Languages
* Spoken terms are now supported on languages whose transcription is provided by external service partners, in addition to languages transcribed by DeepL. The feature remains in beta on all supported languages.
* External-provider languages are marked `"external": true` in the [`GET /v3/languages?resource=voice`](/docs/languages/using-the-languages-api) response. Call with `include=beta&include=external` to see the full list.
* See [Voice API Customization](/docs/voice/overview#customization) for usage details.
## June 3 - Docs MCP Server
* DeepL's developer documentation now exposes an MCP server at `https://developers.deepl.com/mcp`.
* This enables AI tools to search the docs directly and get source-grounded answers about the DeepL API, with no API key required.
* See the [Docs MCP Server](/docs/getting-started/docs-mcp-server) page for setup instructions.
## June 1 - API Status Page
* Launched the [DeepL API Status Page](https://status.deepl.com/?tab=api), a dedicated dashboard for monitoring the operational status and availability of the DeepL API.
* View real-time status and 90-day availability for all API services across all supported regions.
* Supports both Pro and Free API tiers.
* Available in 19 languages.
## May 27 - Custom Reporting Tags in deepl-rb
* The Ruby client library [`deepl-rb`](https://github.com/DeepL/deepl-rb) v3.8.0 now accepts an `additional_headers` argument on `translate` calls, so you can send `X-DeepL-Reporting-Tag` for usage reporting.
* See [Sending Custom Reporting Tags with Client Libraries](/docs/learning-how-tos/cookbook/sending-custom-reporting-tags-from-client-libraries) for the updated Ruby snippet.
## May 26 - API Usage Logger Cookbook
* Added a new cookbook, [API Usage Logger](/docs/learning-how-tos/cookbook/api-usage-logger), showing how to capture per-request DeepL API usage data (billed characters, language pairs, reporting tags, API keys, errors) and explore it through a local Streamlit dashboard.
* Source code on GitHub: [`DeepL/deepl-api-usage-logger`](https://github.com/DeepL/deepl-api-usage-logger).
## May 20 - Custom Tag Usage Analytics
* Added [`GET /v2/admin/analytics/custom-tags`](/api-reference/admin-api/get-custom-tag-usage-analytics) to the Admin API, allowing admins to retrieve usage statistics broken down by custom tags.
* Supports `aggregate_by=period` (default) to aggregate usage over the full date range, or `aggregate_by=day` for daily breakdowns.
* Results are paginated; use the `next_page` integer from the response as the `page` parameter in subsequent requests.
## May 19 - Voice API Spoken Terms
* New optional `spoken_terms_id` parameter on [`POST /v3/voice/realtime`](/api-reference/voice/request-session) to improve transcription of frequently used terms like company-specific terminology, acronyms, product names, and team member names.
* Spoken terms are currently in beta, supported for 18 source languages. Manage them in [DeepL Home](https://www.deepl.com/en/voice/spoken-terms); API management will follow in a future update.
* The new `spoken_terms` feature and the existing `translated_speech` feature are now exposed as beta in [`GET /v3/languages?resource=voice`](/docs/languages/using-the-languages-api), so clients can discover language support programmatically.
* Not yet supported for transcription provided by external service partners (languages marked with ⎋). See [Voice API Customization](/docs/voice/overview#customization) for details.
## May 18 - v3/languages General Availability
* [`GET /v3/languages`](/docs/languages/using-the-languages-api) and [`GET /v3/languages/resources`](/api-reference/languages/retrieve-resources) are now generally available.
* **`/v2/languages` and `/v2/glossary-language-pairs` are now deprecated.** Migrate to `/v3/languages`. See the [migration guide](/docs/languages/migrating-from-v2-languages) for details.
## April 30 - German (Swiss) and French (Canadian) Beta
* `de-CH` (German, Swiss) and `fr-CA` (French, Canadian) have moved to beta.
* During the beta phase, characters translated into these languages are not billed and do not count against your character threshold. Prior to April 30, these languages were generally available and billed normally.
* See [supported languages](../getting-started/supported-languages) for the complete language list.
## April 17 - New Language Variants: German (Swiss) and French (Canadian)
* Added support for two new target language variants: `de-CH` (German, Swiss) and `fr-CA` (French, Canadian) for text translation. These variants are now generally available.
* Use these language codes as `target_lang` values to produce translations tailored to Swiss German and Canadian French.
* Document translation support for these variants will follow in a future release.
* See [supported languages](../getting-started/supported-languages) for the complete language list.
## April 15 - Voice API General Availability
* The [Voice API](/docs/voice/overview) is now available to all DeepL customers with a paid API subscription. This API provides real-time speech transcription and translation and can be used with existing DeepL API keys.
## April 9 - Translation Memory API
* Added support for [translation memories](/docs/customize/using-translation-memories) in the DeepL API. Translation memories store previously translated segments so the same source text produces consistent output across projects .
* New `translation_memory_id` and `translation_memory_threshold` parameters on the [text translation endpoint](/api-reference/translate/request-translation) — pass a translation memory ID to apply stored translations, and set a threshold (0-100) to control how closely source text must match a stored segment.
* New [`GET /v3/translation_memories`](/api-reference/translation-memory/list-translation-memories) endpoint to retrieve translation memories associated with your account.
* Support for uploading, modifying, and deleting translation memories via the API will follow.
## April 2 - Voice API Language Expansion
* Added 6 new languages to the [Voice API](/docs/voice/overview) for transcription: Bengali (`bn`), Croatian (`hr`), Dutch (`nl`), Irish (`ga`), Maltese (`mt`), and Tagalog (`tl`). Five of these are provided by external service partners; Dutch is provided by DeepL.
* All supported voice languages can now be used as source languages for transcription. Previously, source language selection was restricted to a subset of the supported languages.
## March 31 - Write API Improvements
* Improved overall quality of the models used in [DeepL API for Write](/api-reference/improve-text/)
* Expanded Write `target_lang` support to Japanese (`JA`), Korean (`KO`) and simplified Chinese (`ZH` or `zh-Hans`)
* Expanded Write support for `writing_style` and `tone` to Spanish (`ES`), Italian (`IT`), French (`FR`), Portuguese (`pt-PT`) and Brazilian Portuguese (`pt-BR`)
* Allow sending Write texts in multiple source languages in the same request when no `target_lang` is set
## March 26 - Expanded Style Rules API
* Added 5 new endpoints for style rule list operations: `POST /v3/style_rules`, `GET /v3/style_rules/{style_id}`, `PATCH /v3/style_rules/{style_id}`, `DELETE /v3/style_rules/{style_id}`, and `PUT /v3/style_rules/{style_id}/configured_rules`.
* Added 4 new endpoints for managing custom instructions within style rule lists: `POST /v3/style_rules/{style_id}/custom_instructions`, `GET /v3/style_rules/{style_id}/custom_instructions/{instruction_id}`, `PUT /v3/style_rules/{style_id}/custom_instructions/{instruction_id}`, and `DELETE /v3/style_rules/{style_id}/custom_instructions/{instruction_id}`.
* See the full API reference [here](/docs/customize/using-style-rules).
## March 24 - Write API Character Count Fix
* Fixed an issue where some [DeepL API for Write](/api-reference/improve-text/) requests under-counted usage.
* All characters in Write requests are now correctly counted for billing and reporting purposes.
## February 18 - Additional Languages in `v2/languages`
* New languages added to the [v2/languages endpoint](/api-reference/languages/retrieve-supported-languages), bringing totals to **101 source languages** and **106 target languages**.
* These languages were already supported for translation; this change improves automatic discoverability for API clients.
* Note: 12 supported languages using three-letter base codes (e.g. ACE, CEB, CKB) are not yet included in `v2/languages` for backwards compatibility, but will be available in the upcoming `v3/languages` endpoint.
## January 27 - Voice Usage Limits on Developer Keys
* Added `speech_to_text_milliseconds` to the `/v2/admin/developer-keys/limits` endpoint and the `ApiKey` response schema, so admins can set and read per-key voice usage limits alongside character limits.
* See the [Admin API overview](/docs/admin/overview) for usage.
## January 20 - Legacy Auth Deprecation
Query parameter and request body authentication methods are now [deprecated](/docs/resources/breaking-changes-change-notices/november-2025-deprecation-of-legacy-auth-methods).
* **API Free users**: All requests using legacy auth now return `403 Forbidden`.
* **API Pro users**: Brownout period active with intermittent `403` responses. Full deprecation in early February 2026.
Use the `DeepL-Auth-Key` header instead.
## January 19 - Voice API Speech-to-Speech (Closed Beta)
* New speech-to-speech capability in the [Voice API](/docs/voice/overview): synthesize translated audio (TTS output) alongside transcription and text translation in the same WebSocket session.
* New request parameters on [`POST /v3/voice/realtime`](/api-reference/voice/request-session): `target_media_languages` (target languages for synthesized speech), `target_media_content_type` (audio format), and `target_media_voice` (voice selection).
* Translated speech is in closed beta and not included in standard API subscriptions.
## January 8 - 81 New Languages in GA
* Promoted all 81 beta languages to standard language support. These languages are now part of the main source and target language lists.
* These languages are compatible with all `model_type` values including `latency_optimized`.
* The `enable_beta_languages` parameter is maintained for backward compatibility but has no effect.
See [here](../getting-started/supported-languages) for the complete language list.
## December 11 - Voice Usage in `/v2/usage` and Analytics
* `/v2/usage` now returns `speech_to_text_milliseconds_count` and `speech_to_text_milliseconds_limit` for API Pro users, so customers using the Voice API can monitor consumption against their plan.
* Each product usage item now includes `billing_unit`, `api_key_unit_count`, and `account_unit_count`, making per-product reporting consistent across characters and voice milliseconds.
* The same `speech_to_text_milliseconds` field was added to usage reports and analytics components used by the [Admin API analytics endpoint](/api-reference/admin-api/get-usage-analytics).
## November 10 - Voice API Initial Release
* Initial release of the Voice API: `/v1/voice/realtime` (REST) and `/v1/voice/realtime/connect` (WebSocket) for real-time speech transcription and translation. The Voice API is generally available as of [April 15, 2026](#april-15---voice-api-general-availability).
* Published a new AsyncAPI specification (`voice.asyncapi.yaml` / `voice.asyncapi.json`) documenting the WebSocket streaming protocol. See the [WebSocket Streaming reference](/api-reference/voice/websocket-streaming).
## November 6 - HE, TH, and VI in `/v2/languages`
* Hebrew (`HE`), Thai (`TH`), and Vietnamese (`VI`) now appear in the [`/v2/languages`](/api-reference/languages/retrieve-supported-languages) response, since they support document translation in addition to text translation.
## Q4 2025
* Added API reference for the Voice API
* Add contextual menu in the to make it easier to copy any API documentation page as context for AI tools
* Add support for style rules in the API to programmatically get your created style rules and translate with them.
* Overhauled the tag-handling algorithm that backs translation of XML and HTML in the DeepL API. To enable it and benefit from all the improvements, set the `tag_handling_version` parameter to `v2` in the text translation API. See [Translating XML](/docs/translate/translating-xml) for more information.
* Added new usage analytics endpoint in the Admin API, including key-level reporting. See [here](/api-reference/admin-api/get-usage-analytics) for more details
* Enabled support for multiple admins in an API subscription
* Added support for 75 new languages, initially through the `enable_beta_languages` parameter, for text and document translation. Beta languages are not billed during the beta phase and do not yet support glossaries or formality. See [here](../getting-started/supported-languages) for more information
* Added HE, TH and VI to the languages endpoint, since they are now also available in document translation
* Added 6 additional beta languages, for text and document translation. See the previous note about 75 new languages.
* Added a new parameter to the text translation API to allow custom instructions, making it possible to customize the translation behavior (e.g. \["Use a friendly, diplomatic tone"])
* Added support for JPEG and PNG images in [document translation](/api-reference/document/upload-and-translate-a-document), currently in Beta.
## Q3 2025
* Creation of an admin API, making it possible to manage API keys programmatically. See the [Admin API overview](/docs/admin/overview) for more information
* Added support for new language: ES-419 (Latin American Spanish). For this release, this language will be available for the API in next-gen models only. See [here](../getting-started/supported-languages) for more information.
* Refreshed our API documentation and added new try-it explorers to support interacting with our APIs. See [here](/api-reference) to try it out!
## Q2 2025
* Added support for new language: TH (Thai). For this release, this language will only support text translation. See [here](../getting-started/supported-languages) for more information.
* Added support for new languages: HE (Hebrew) and VI (Vietnamese). For this release, these languages will be available for Pro v2 API in next-gen models only. See [here](../getting-started/supported-languages) for more information.
* Added improvements to API glossaries, including the ability to edit glossaries and create multilingual glossaries. Learn more [here](/api-reference/multilingual-glossaries/).
* Added new events to audit logs for Pro API customers (API key management, cost control and usage limit changes)
* Improvements to the `/usage` endpoint for Pro API customers ([API reference](/api-reference/usage-and-quota/check-usage-and-limits))
* Improvements to key-level usage reporting, making it possible to pull reports with a custom date range and to group data by calendar day. Learn more in the [usage data guide](/docs/admin/retrieving-usage-data#csv-export).
## Q1 2025
* Added support for API key-level usage limits, making it possible to set a character limit at the API key-level. Learn more [here](/docs/admin/managing-api-keys#set-a-key-level-usage-limit).
* DeepL API for Write is generally available to Pro API customers, making it possible to improve texts in (at the time of release) 6 different languages. Learn more and get started [here](/api-reference/improve-text/).
## Q4 2024
* Added the `model_type` parameter, allowing users to translate text with DeepL's "next-gen" translation models. More information can be found [here](/docs/translate/understanding-model-types).
## Q3 2024
* Added the `show_billed_characters` parameter for text translation, allowing users to optionally include the number of billed characters in the API response. [Learn more here](/api-reference/translate/request-translation).
* Added support for a new language for text translation: ZH-HANT (Traditional Chinese). As of this initial release, document translation is not supported for Traditional Chinese. More information is available [here](/docs/getting-started/supported-languages).
* An official Ruby client library, evolved from a community-written library by Daniel Herzog. You can download it [here](https://rubygems.org/gems/deepl-rb) or find the source code on our [GitHub page](https://github.com/DeepL/deepl-rb).
* Added Romanian (`RO`) as a supported [glossary](/docs/customize/managing-glossaries) language.
## Q2 2024
* Added support for DOCX and PPTX document minification to the PHP client library, making it possible for users to translate documents that exceed DeepL's file size limit due to embedded media. [Learn more here](https://github.com/DeepL/deepl-php?tab=readme-ov-file#document-minification).
* Added support for API key-level usage reporting. More information is available in the [multiple API keys guide](https://developers.deepl.com/multiple-api-keys#download-a-report-with-key-level-usage).
* Launched DeepL Pro in [165 new markets](https://www.deepl.com/en/blog/deepl-pro-expands-165-new-markets), bringing the total number of markets where DeepL Pro is available to 228. This means users with billing addresses in these markets can create DeepL Pro API and Free API subscriptions.
* Added support for new glossary languages: DA (Danish), NB (Norwegian Bokmål), and Swedish (SV).
* Moved the `context` parameter from [alpha](/docs/getting-started/alpha-and-beta-features) status to general availability. More information about the context parameter is available in the "Request body descriptions" table [here](/api-reference/translate/request-translation).
* Added support for SRT (`srt`) files in [document translation](/api-reference/document/upload-and-translate-a-document).
## Q1 2024
* Added support for multiple API keys in a single account for Pro API and Free API users. More information is available in the [multiple API keys guide](https://developers.deepl.com/multiple-api-keys).
* Added support for a new language for text translation: AR (Arabic). As of this initial release, document translation is not supported for Arabic. More information is available [here](/docs/getting-started/supported-languages).
* Added support for Korean (KO) as a [glossary](/docs/customize/managing-glossaries) language, increasing the number of supported glossary language pairs from 55 to 66.
## Q4 2023
* Added support for Microsoft Excel (`xlsx`) files in [document translation](/api-reference/document/upload-and-translate-a-document).
* Released the `context` parameter as an [alpha feature](/docs/getting-started/alpha-and-beta-features) for text translation (see [Request Body Descriptions table](/api-reference/translate/request-translation) for more information).
## Q3 2023
* Launched DeepL Pro in South Korea ([blog post here](https://www.deepl.com/en/blog/deepl-pro-available-in-south-korea)). This means users with billing addresses in South Korea can [create DeepL Pro API and Free API subscriptions](https://www.deepl.com/ko/pro#developer).
* Added support for Portuguese (PT), Russian (RU), and Chinese (ZH) as [glossary](/docs/customize/managing-glossaries) languages, increasing the number of supported glossary language pairs from 28 to 55.
* Added support for JSON-encoded requests for all remaining endpoints (*note that document upload for document translation still requires*`multipart/form-data`).
## Q2 2023
* Added support for user-provided http clients in [PHP client library](https://github.com/DeepL/deepl-php).
* Released an official [DeepL API Postman collection](https://www.postman.com/deepl-api/workspace/deepl-api-developers/overview).
* Added formality support for Japanese (JA) in [text](/api-reference/translate/request-translation) and [document](/api-reference/document/upload-and-translate-a-document) translation.
* Released in-house PDF translation; removed requirement to send data to the US when translating PDF documents ([blog post](https://www.deepl.com/en/blog/deepl-launches-in-house-pdf-translation-for-improved-security-and-efficiency)).
## Q1 2023
* Released an official [DeepL Custom Connector](https://support.deepl.com/hc/en-us/articles/8644041855516-DeepL-API-custom-connector-for-Microsoft-Power-Automate) for Microsoft Power Automate.
* Added support for glossaries in any combination of two languages from the following list: EN (English), DE (German), FR (French), IT (Italian), PL (Polish), NL (Dutch), ES (Spanish, JA (Japanese). This represents an increase from 8 to 28 supported glossary language pairs.
* Added XLIFF as a [document translation](/api-reference/document/upload-and-translate-a-document) format (*note that only documents from version 2.0 are supported, and there is no support for the legacy 1.2 format*).
* Added support for new languages for text and document translation: KO (Korean) and NB (Norwegian Bokmål). [Blog post here](https://www.deepl.com/en/blog/welcome-korean-and-norwegian).
## Q4 2022
* Moved [HTML handling](/docs/translate/translating-html) out of beta after fixing the most commonly reported user issues.
* Released an official [Java client library](https://github.com/DeepL/deepl-java).
* Added support for JSON-encoded requests for [text translation](/api-reference/translate/request-translation), [usage](/api-reference/usage-and-quota/check-usage-and-limits), and [languages](/api-reference/languages/retrieve-supported-languages) endpoints.
## Q3 2022
* Released an official [PHP client library](https://github.com/DeepL/deepl-php).
* Added support for a new language for [text](/api-reference/translate/request-translation) and [document](/api-reference/document/upload-and-translate-a-document) translation: UK (Ukrainian). [Blog post here](https://www.deepl.com/en/blog/deepl-learns-ukrainian).
## Before Q3 2022
* Released an official DeepL API [OpenAPI spec](https://github.com/DeepL/openapi).
* Released an official [NodeJS client library](https://github.com/DeepL/deepl-node).
* Released an official [.NET client library](https://github.com/DeepL/deepl-dotnet).
* Released an official [Python client library](https://github.com/DeepL/deepl-python).
* Added support for [glossaries](/docs/customize/managing-glossaries) in the DeepL API.
* Released an open-source sample project for [translating with DeepL in Google Sheets](https://github.com/DeepL/google-sheets-example).
* Added the `prefer_less` and `prefer_more` formality options on [text translation](/api-reference/translate/request-translation), giving more graceful fallback than the strict `more` and `less` values for languages without full formality support.
* Added CSV as an entries format for creating [glossaries](/docs/customize/managing-glossaries), alongside the existing tab-separated format.
* Removed the previous limit of 50 `text` parameters per [text translation](/api-reference/translate/request-translation) request.
* ...and much more :)
# Usage and limits
Source: https://developers.deepl.com/docs/resources/usage-limits
### API Limits
| Type of limit | Maximum limit |
| ------------------ | ----------------------------------------------- |
| Header size | 16 KiB (16\*1024 bytes) |
| Total request size | 128 KiB (128\*1024 bytes) |
| Character count | 500,000 characters per month for DeepL API Free |
### Maximum Upload Limits Per Document Format
| File Format | DeepL API Free | DeepL API Pro |
| ----------------------- | ------------------------------ | -------------------------------- |
| Word (.docx / .doc) | 10 MB 500,000 characters | 100 MB 1 million characters |
| PowerPoint (.pptx) | 10 MB 500,000 characters | 100 MB 1 million characters |
| Excel (.xlsx) | 10 MB 500,000 characters | 30 MB 1 million characters |
| PDF (.pdf) | 10 MB 500,000 characters | 100 MB 1 million characters |
| Text (.txt) | 1 MB 500,000 characters | 1 MB 1 million characters |
| HTML (.html) | 5 MB 500,000 characters | 5 MB 1 million characters |
| IDML (.idml) | 10 MB 500,000 characters | 30 MB 1 million characters |
| MIF (.mif) | 10 MB 500,000 characters | 30 MB 1 million characters |
| XML (.xml) | 10 MB 500,000 characters | 10 MB 1 million characters |
| JSON (.json) | 1 MB 500,000 characters | 1 MB 1 million characters |
| DITA (.dita) | 5 MB 500,000 characters | 5 MB 1 million characters |
| XLIFF (.xlf/.xliff)\* | 10 MB 500,000 characters | 10 MB 1 million characters |
| SRT (.srt) | 150 KB 500,000 characters | 150 KB 1 million characters |
| Images (.jpeg/.png)\*\* | 3 MB 500,000 characters | 3 MB 1 million characters |
\*DeepL supports XLIFF versions 1.2, 2.0, and 2.1 (2.1 shares the 2.0 core namespace).
\*\*Image translation is currently in Beta. During the Beta phase, characters translated in image file formats are not billed and not counted against your character threshold.
### Your Usage
Retrieve usage information within the current billing period together with the corresponding account limits.
Usage is returned and tracked for translated characters. Note that for [text translation](/api-reference/translate/request-translation), characters are still counted toward billing when the source and target languages are equal.
Character usage includes both text and document translations, and is measured by the source text length in Unicode code points. For example, "A", "Δ", "あ", and "深" are each counted as a single character. The `character_count` field returned by the [`/usage` endpoint](/api-reference/usage-and-quota/check-usage-and-limits) is a sum of Translate API and Write API characters.
# Controlling Writing Style and Tone
Source: https://developers.deepl.com/docs/translate/controlling-writing-style-and-tone
Steer Write API output with the writing_style and tone parameters: available values, per-language support, and prefer_ fallback behavior.
The [`/v2/write/rephrase` endpoint](/api-reference/improve-text/request-text-improvement) accepts two parameters that steer how your text is rewritten: `writing_style` changes the register of the text, and `tone` changes how it sounds. A request can include one or the other, but not both.
## Writing styles
| Value | Fallback variant |
| :--------- | :--------------------------------- |
| `simple` | `prefer_simple` |
| `business` | `prefer_business` |
| `academic` | `prefer_academic` |
| `casual` | `prefer_casual` |
| `default` | (same as omitting `writing_style`) |
## Tones
| Value | Fallback variant |
| :------------- | :------------------------ |
| `enthusiastic` | `prefer_enthusiastic` |
| `friendly` | `prefer_friendly` |
| `confident` | `prefer_confident` |
| `diplomatic` | `prefer_diplomatic` |
| `default` | (same as omitting `tone`) |
## Language support and fallback behavior
Styles and tones are currently supported for the target languages `de`, `en-GB`, `en-US`, `es`, `fr`, `it`, `pt-BR`, and `pt-PT`. To check support dynamically, call [`GET /v3/languages?resource=write`](/docs/languages/using-the-languages-api) and look for the `writing_style` or `tone` feature key on the target language.
The `prefer_` variants fall back to `default` when the target language doesn't support styles or tones; the non-prefixed values return an HTTP 400 error in that case. Use the `prefer_` variants when you don't set a `target_lang` explicitly, since the detected language may not support styles or tones.
## Example
This request rewrites the same text in a diplomatic tone:
```sh Sample request theme={null}
curl -X POST https://api.deepl.com/v2/write/rephrase \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"text": [
"Your proposal misses the point entirely."
],
"target_lang": "en-US",
"tone": "prefer_diplomatic"
}'
```
```json Sample response theme={null}
{
"improvements": [
{
"text": "It seems that your proposal may not fully capture the essence of the issue at hand.",
"detected_source_language": "en",
"target_language": "en-US"
}
]
}
```
New to the Write API? Start with the [Write Quickstart](/docs/translate/write-quickstart).
# Translate
Source: https://developers.deepl.com/docs/translate/overview
Translate text strings and complete documents with the DeepL API. Find quickstarts, markup handling guides, and customization options.
The Translate API converts text between any of the [supported languages](/docs/getting-started/supported-languages) through two endpoints:
* **Text translation** (`/v2/translate`): translate one or many strings per request, with automatic source language detection. Suited for UI strings, messages, and any text your application handles directly.
* **Document translation** (`/v2/document`): upload complete files, including Word, PowerPoint, PDF, and HTML, and download the translation with the original formatting intact.
## Start here
Send your first translation request and batch multiple strings into one call.
Upload a Word document, poll its status, and download the translated file.
Improve translation quality for short or ambiguous text by passing surrounding context.
Translate markup without breaking it: tag handling for XML, HTML, and structured content.
Full request and response schemas for the text and document translation endpoints.
## Customize translations
Beyond per-request parameters, DeepL's customization features let you tailor translations to your domain and keep terminology consistent: glossaries, style rules, custom instructions, and translation memories. They all work with both text and document translation; see the [Customize tab](/docs/customize/overview) for guides on each.
# Translate Documents Quickstart
Source: https://developers.deepl.com/docs/translate/translate-documents-quickstart
Upload a Word document to the DeepL API, poll its translation status, and download the translated file, in three curl calls or one client library call.
In this tutorial, you'll translate a complete document with the DeepL API: upload a file, poll until the translation is done, and download the result. By the end, you'll have run the full asynchronous flow with curl and seen how the client libraries collapse it into a single call.
Document translation preserves the file's formatting and supports Word, PowerPoint, Excel, PDF, HTML, XLIFF, SRT subtitles, and more. See the [full list of supported formats](/api-reference/document/upload-and-translate-a-document).
## Prerequisites
* A DeepL API account and your API key from [your account settings](https://www.deepl.com/your-account/keys). New to the API? Start with the [Translate Text Quickstart](/docs/translate/translate-text-quickstart), which covers signup in more detail.
* `curl` installed on your machine
* A document to translate. The examples use a Word file named `order-confirmation.docx`; any supported format works.
If you're on a free API plan, replace `https://api.deepl.com` with `https://api-free.deepl.com` in curl examples. Client libraries detect your account type and pick the correct URL automatically.
## Building with an AI coding agent?
Wire it up to the [DeepL Docs MCP Server](/docs/getting-started/docs-mcp-server) so it can search and read this documentation while it writes code. In Claude Code:
```bash theme={null}
claude mcp add --transport http deepl-docs https://developers.deepl.com/mcp
```
Then describe what you want to build. To get the same result as this tutorial, paste:
```text wrap theme={null}
Using the DeepL API, write a script that uploads order-confirmation.docx for translation to German, polls the status until it's done, and downloads the translated file.
```
Setup instructions for Claude Desktop, Cursor, VS Code, and other MCP clients are on the [Docs MCP Server page](/docs/getting-started/docs-mcp-server).
## Step 1: Upload the document
Document translation runs asynchronously, so the flow has three calls: upload the file, check the status, and download the result. Start by uploading the file as `multipart/form-data` with a `target_lang`:
```sh Sample request theme={null}
export API_KEY={YOUR_API_KEY}
curl -X POST https://api.deepl.com/v2/document \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--form 'target_lang=DE' \
--form 'file=@order-confirmation.docx'
```
```json Sample response theme={null}
{
"document_id": "04DE5AD98A02647D83285A36021911C6",
"document_key": "0CB0054F1C132C1625B392EADDA41CB754A742822F6877173029A6C487E7F60A"
}
```
The response returns as soon as the upload completes, while the translation continues in the background. Store both values: the `document_id` identifies the translation, and the `document_key` authorizes the status and download calls in the next steps.
As with text translation, the source language is detected automatically, or you can pin it with an optional `source_lang` form field.
## Step 2: Poll the translation status
Check the status by sending the `document_key` to the `/v2/document/{document_id}` endpoint:
```sh Sample request theme={null}
curl -X POST https://api.deepl.com/v2/document/04DE5AD98A02647D83285A36021911C6 \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"document_key": "0CB0054F1C132C1625B392EADDA41CB754A742822F6877173029A6C487E7F60A"
}'
```
While the document is being translated, the `status` field is `queued` or `translating`:
```json Sample response: still translating theme={null}
{
"document_id": "04DE5AD98A02647D83285A36021911C6",
"status": "translating",
"seconds_remaining": 20
}
```
Repeat the call at regular intervals or with exponential backoff until the status is `done`. Small documents typically finish in seconds; larger ones can take a minute or two. Treat `seconds_remaining` as a rough estimate only.
```json Sample response: done theme={null}
{
"document_id": "04DE5AD98A02647D83285A36021911C6",
"status": "done",
"billed_characters": 1337
}
```
A status of `error` comes with a `message` field explaining what went wrong, for example when the source and target language are the same.
## Step 3: Download the translated file
Once the status is `done`, download the result from the `/result` endpoint and save it to a file:
```sh Sample request theme={null}
curl -X POST https://api.deepl.com/v2/document/04DE5AD98A02647D83285A36021911C6/result \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"document_key": "0CB0054F1C132C1625B392EADDA41CB754A742822F6877173029A6C487E7F60A"
}' \
--output order-confirmation-de.docx
```
Open `order-confirmation-de.docx` and you'll find the German translation with the original layout intact.
For privacy reasons, the document is removed from DeepL's servers once you download it, so it can't be downloaded twice. To translate the same file again, start over from step 1. Download promptly: leaving too many finished translations unretrieved can cause new upload requests to fail with HTTP 429.
Every submitted `docx`, `doc`, `pptx`, `xlsx`, or `pdf` file is billed a minimum of 50,000 characters, regardless of how much text it contains.
## Or: one call with a client library
The [official client libraries](/docs/getting-started/client-libraries) wrap all three steps, upload, polling, and download, in a single method call:
```py Sample request theme={null}
import deepl
auth_key = "{YOUR_API_KEY}" # replace with your key
deepl_client = deepl.DeepLClient(auth_key)
deepl_client.translate_document_from_filepath(
"order-confirmation.docx",
"order-confirmation-de.docx",
target_lang="DE",
)
```
```javascript Sample request theme={null}
import * as deepl from 'deepl-node';
const authKey = "{YOUR_API_KEY}"; // replace with your key
const deeplClient = new deepl.DeepLClient(authKey);
(async () => {
await deeplClient.translateDocument(
'order-confirmation.docx',
'order-confirmation-de.docx',
null,
'de'
);
})();
```
The PHP, C#, Java, and Ruby libraries offer the same convenience method; see each [library's documentation](/docs/getting-started/client-libraries) for details.
## Next steps
You've now run the complete document translation flow. To keep going:
* Read the [document translation guide](/docs/best-practices/document-translations) for format-specific behavior (XML, XLIFF, JSON, IDML) and error handling
* Convert formats on the way through, like PDF in and editable Word out, with the `output_format` parameter in the [`/document` reference](/api-reference/document/upload-and-translate-a-document)
* Enforce your terminology with [glossaries](/docs/customize/glossaries-in-the-real-world)
* Check the [upload size limits per format](/docs/resources/usage-limits#maximum-upload-limits-per-document-format) before going to production
# Translate Text Quickstart
Source: https://developers.deepl.com/docs/translate/translate-text-quickstart
Send your first text translation request to the DeepL API and batch multiple strings into a single call.
In this tutorial, you'll translate your first text with the DeepL API, then batch several strings into a single request. By the end, you'll have made the two most common types of text translation request, using curl or the official client library for your language.
## Prerequisites
* A DeepL API account. Visit [our plans page](https://www.deepl.com/pro-api#api-pricing), choose a plan, and sign up. If you already have a DeepL Translator account, you need to log out and [create a separate account](https://support.deepl.com/hc/articles/360019358999-Change-plan) for the API.
* Your API key, which you can find in [your account settings](https://www.deepl.com/your-account/keys). To learn more about keys, see [Authentication](/docs/getting-started/auth).
* `curl`, or one of the [official client libraries](/docs/getting-started/client-libraries) for Python, JavaScript, PHP, C#, Java, or Ruby.
If you're on a free API plan, replace `https://api.deepl.com` with `https://api-free.deepl.com` in curl examples. Client libraries detect your account type and pick the correct URL automatically.
## Building with an AI coding agent?
Wire it up to the [DeepL Docs MCP Server](/docs/getting-started/docs-mcp-server) so it can search and read this documentation while it writes code. In Claude Code:
```bash theme={null}
claude mcp add --transport http deepl-docs https://developers.deepl.com/mcp
```
Then describe what you want to build. To get the same result as this tutorial, paste:
```text wrap theme={null}
Using the DeepL API, write a script that translates a list of English strings to German and prints the results.
```
Setup instructions for Claude Desktop, Cursor, VS Code, and other MCP clients are on the [Docs MCP Server page](/docs/getting-started/docs-mcp-server).
## Step 1: Send your first translation request
A translation request needs only two parameters: `text`, the text to translate, and `target_lang`, the language you're translating to. You don't need to specify the source language. DeepL detects it and returns it in the response as `detected_source_language`. If your text is very short or mixes languages, you can pin the source with the optional `source_lang` parameter.
Language codes follow ISO 639, like `DE` for German or `JA` for Japanese, and are case-insensitive. Some target languages support regional variants, like `en-US` or `pt-BR`. See the full list of [supported languages](/docs/getting-started/supported-languages).
```sh Set the API key theme={null}
export API_KEY={YOUR_API_KEY}
```
```sh Sample request theme={null}
curl -X POST https://api.deepl.com/v2/translate \
--header "Content-Type: application/json" \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--data '{
"text": ["Your order has shipped and will arrive on Tuesday."],
"target_lang": "DE"
}'
```
```json Sample response theme={null}
{
"translations": [
{
"detected_source_language": "EN",
"text": "Ihre Bestellung wurde versandt und kommt am Dienstag an."
}
]
}
```
```sh Install client library theme={null}
pip install deepl
```
```py Sample request theme={null}
import deepl
auth_key = "{YOUR_API_KEY}" # replace with your key
deepl_client = deepl.DeepLClient(auth_key)
result = deepl_client.translate_text(
"Your order has shipped and will arrive on Tuesday.",
target_lang="DE",
)
print(result.text)
```
```text Sample output theme={null}
Ihre Bestellung wurde versandt und kommt am Dienstag an.
```
```sh Install client library theme={null}
npm install deepl-node
```
```javascript Sample request theme={null}
import * as deepl from 'deepl-node';
const authKey = "{YOUR_API_KEY}"; // replace with your key
const deeplClient = new deepl.DeepLClient(authKey);
(async () => {
const result = await deeplClient.translateText(
'Your order has shipped and will arrive on Tuesday.',
null,
'de'
);
console.log(result.text);
})();
```
```text Sample output theme={null}
Ihre Bestellung wurde versandt und kommt am Dienstag an.
```
```sh Install client library theme={null}
composer require deeplcom/deepl-php
```
```php Sample request theme={null}
require_once 'vendor/autoload.php';
use DeepL\Client;
$authKey = "{YOUR_API_KEY}"; // replace with your key
$deeplClient = new DeepL\DeepLClient($authKey);
$result = $deeplClient->translateText(
'Your order has shipped and will arrive on Tuesday.',
null,
'de'
);
echo $result->text;
```
```text Sample output theme={null}
Ihre Bestellung wurde versandt und kommt am Dienstag an.
```
```sh Install client library theme={null}
dotnet add package DeepL.net
```
```csharp Sample request theme={null}
using DeepL; // this imports the DeepL namespace. Use the code below in your main program.
var authKey = "{YOUR_API_KEY}"; // replace with your key
var client = new DeepLClient(authKey);
var translatedText = await client.TranslateTextAsync(
"Your order has shipped and will arrive on Tuesday.",
null,
LanguageCode.German);
Console.WriteLine(translatedText);
```
```text Sample output theme={null}
Ihre Bestellung wurde versandt und kommt am Dienstag an.
```
```java Install client library theme={null}
// For instructions on installing the DeepL Java library,
// see https://github.com/DeepL/deepl-java?tab=readme-ov-file#installation
```
```java Sample request theme={null}
import com.deepl.api.*;
public class Main {
public static void main(String[] args) throws DeepLException, InterruptedException {
String authKey = "{YOUR_API_KEY}"; // replace with your key
DeepLClient client = new DeepLClient(authKey);
TextResult result = client.translateText(
"Your order has shipped and will arrive on Tuesday.", null, "de");
System.out.println(result.getText());
}
}
```
```text Sample output theme={null}
Ihre Bestellung wurde versandt und kommt am Dienstag an.
```
```sh Install client library theme={null}
gem install deepl-rb
```
```ruby Sample request theme={null}
require 'deepl'
DeepL.configure do |config|
config.auth_key = '{YOUR_API_KEY}' # replace with your key
end
translation = DeepL.translate 'Your order has shipped and will arrive on Tuesday.', nil, 'DE'
puts translation.text
```
```text Sample output theme={null}
Ihre Bestellung wurde versandt und kommt am Dienstag an.
```
The examples hardcode the key to keep them short. In production code, store your API key in an environment variable instead.
For security reasons, you can't call the DeepL API directly from client-side JavaScript. During testing or prototyping, route requests through [a simple proxy](/docs/learning-how-tos/cookbook/nodejs-proxy) instead.
## Step 2: Translate multiple strings in one call
The `text` parameter is an array, so one request can carry many strings, like every notification in a template file. Each string is translated separately and the response preserves their order. If you don't set `source_lang`, DeepL detects the language of each string individually.
```sh Sample request theme={null}
curl -X POST https://api.deepl.com/v2/translate \
--header "Content-Type: application/json" \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--data '{
"text": [
"Your order has shipped.",
"Estimated delivery: Tuesday, July 14."
],
"target_lang": "DE"
}'
```
```json Sample response theme={null}
{
"translations": [
{
"detected_source_language": "EN",
"text": "Ihre Bestellung wurde versandt."
},
{
"detected_source_language": "EN",
"text": "Voraussichtliche Lieferung: Dienstag, 14. Juli."
}
]
}
```
The client libraries accept a list of strings in the same `translate_text` methods you used in step 1.
The total request body is limited to 128 KiB. For anything larger, or for files whose formatting should be preserved, translate a document instead.
## Next steps
You've now covered the core text translation workflow: single strings and batches. To keep going:
* Translate entire files, formatting included, with the [Translate Documents Quickstart](/docs/translate/translate-documents-quickstart)
* Try requests with more parameters in [our playground](https://developers.deepl.com/api-reference/translate/request-translation?playground=open) or [Postman](/docs/getting-started/test-your-api-requests-with-postman)
* Improve translation quality for short or ambiguous text with the [context parameter](/docs/learning-how-tos/examples-and-guides/how-to-use-context-parameter)
* Choose between speed- and quality-optimized models with the `model_type` parameter, documented in the [`/translate` reference](/api-reference/translate/request-translation)
* Translate into regional variants like `pt-BR` with the [language variants guide](/docs/learning-how-tos/examples-and-guides/translating-between-variants)
* Enforce your terminology with [glossaries](/docs/customize/glossaries-in-the-real-world)
* Check [usage and limits](/docs/resources/usage-limits) before going to production
# Translating HTML
Source: https://developers.deepl.com/docs/translate/translating-html
Learn how to translate HTML content with the DeepL API and exclude specific elements from translation.
To translate HTML content, set the `tag_handling` parameter to `html`. The API extracts the text from the HTML structure, translates it, and places the translation back into the structure. Without `tag_handling`, tags are treated as regular text.
Set `tag_handling_version` to `v2` to use the improved tag handling algorithm. HTML input is never strictly parsed, so invalid HTML doesn't cause errors in either version. For version details and defaults, see the [`tag_handling_version` parameter](/api-reference/translate/request-translation).
```bash Example request theme={null}
curl -X POST https://api.deepl.com/v2/translate \
--header "Content-Type: application/json" \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--data '{
"text": ["This is a premium feature.
"],
"target_lang": "DE",
"tag_handling": "html",
"tag_handling_version": "v2"
}'
```
```json Example response theme={null}
{
"translations": [
{
"detected_source_language": "EN",
"text": "Dies ist eine Premium -Funktion.
",
"tag_handling_version": "v2"
}
]
}
```
You don't need to set `split_sentences` for HTML: with `tag_handling=html` it defaults to `nonewlines`, which splits sentences on punctuation only and gives the best translation quality for HTML. To also split sentences on newlines, set `split_sentences=1`.
To translate non-HTML XML content, see [Translating XML](/docs/translate/translating-xml).
## Exclude elements from translation
To exclude an element from translation, add the `translate="no"` or `class="notranslate"` attribute to it. In the following example, `translate="no"` prevents translation of the paragraph:
```markup Example request theme={null}
My First Heading
My first paragraph.
```
```markup Example response theme={null}
Meine erste Überschrift
My first paragraph.
```
# Translating Large Volumes of Text
Source: https://developers.deepl.com/docs/translate/translating-large-volumes
Batch texts, control sentence splitting, and parallelize requests to translate high volumes efficiently with the text translation endpoint.
The [`/v2/translate` endpoint](/api-reference/translate/request-translation) accepts up to 50 texts per request and request bodies up to 128 KiB. This guide shows how to make the most of each request when you have a lot of text to translate: sending whole paragraphs, batching texts, running requests in parallel, and protecting content that shouldn't be translated.
## Send whole paragraphs as one text
If your text is contiguous, submit entire paragraphs in a single `text` value. Before translating, the engine splits the text into sentences, normally on punctuation marks and newlines, and returns the whole translated paragraph. Don't assume every period acts as a sentence separator; the engine handles abbreviations and similar cases.
```sh Example request theme={null}
curl -X POST https://api.deepl.com/v2/translate \
--header "Content-Type: application/json" \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--data '{
"text": ["The table is green. The chair is black."],
"target_lang": "DE"
}'
```
```json Example response theme={null}
{
"translations": [
{
"detected_source_language": "EN",
"text": "Der Tisch ist grün. Der Stuhl ist schwarz."
}
]
}
```
## Control sentence splitting
Automatic splitting can occasionally divide what is really a single sentence, especially in text with uncommon character sequences that contain punctuation. The `split_sentences` parameter controls this behavior:
| Value | Behavior |
| :----------- | :----------------------------------------------------------- |
| `1` | Split on punctuation and newlines (default) |
| `nonewlines` | Split on punctuation only (default when `tag_handling=html`) |
| `0` | No splitting; the whole input is treated as one sentence |
If your application already sends exactly one sentence per `text` value, set `split_sentences` to `0` to prevent unintended splits. With splitting disabled, overlong inputs are cut off rather than translated, so split long text into sentences yourself before submitting.
Newlines split sentences under the default setting. If your text contains line breaks mid-sentence, either clean them up before sending or use `split_sentences=nonewlines`.
## Batch up to 50 texts per request
The `text` array can carry up to 50 entries per request. Translations come back in the same order:
```sh Example request theme={null}
curl -X POST https://api.deepl.com/v2/translate \
--header "Content-Type: application/json" \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--data '{
"text": [
"This is the first sentence.",
"This is the second sentence.",
"This is the third sentence."
],
"target_lang": "DE"
}'
```
```json Example response theme={null}
{
"translations": [
{
"detected_source_language": "EN",
"text": "Das ist der erste Satz."
},
{
"detected_source_language": "EN",
"text": "Das ist der zweite Satz."
},
{
"detected_source_language": "EN",
"text": "Dies ist der dritte Satz."
}
]
}
```
Each text in the array is translated independently; texts don't share context with each other. If one text would help translate another, like a headline and its article body, combine them into one text or pass the shared information through the [`context` parameter](/docs/learning-how-tos/examples-and-guides/how-to-use-context-parameter), which applies to every text in the request.
## Run requests in parallel
For volumes beyond what batching covers, send multiple requests concurrently from several threads or processes. Watch for HTTP 429 responses and back off accordingly; see [error handling best practices](/docs/best-practices/error-handling) for retry strategies.
## Protect embedded markers
Uncommon character sequences that act as markers in your system, like placeholders or template syntax, might get translated or removed, corrupting your structure. Either split your text so markers don't need to be sent, or convert markers to XML tags and enable [XML handling](/docs/translate/translating-xml) or [HTML handling](/docs/translate/translating-html).
## When to switch to document translation
The total request body is limited to 128 KiB, and a `text` array is capped at 50 entries. For complete files, or text that exceeds these limits, use [document translation](/docs/translate/translate-documents-quickstart) instead: upload limits are far higher and formatting is preserved. See [usage and limits](/docs/resources/usage-limits) for the exact caps per plan.
# Translating XML
Source: https://developers.deepl.com/docs/translate/translating-xml
Learn how to translate XML content with the DeepL API while preserving its structure, and how to control sentence splitting.
To translate XML content, set the `tag_handling` parameter to `xml`. The API extracts the text from the XML structure, translates it, and places the translation back into the structure. Without `tag_handling`, tags are treated as regular text.
Set `tag_handling_version` to `v2` to use the improved tag handling algorithm. For version details and defaults, see the [`tag_handling_version` parameter](/api-reference/translate/request-translation).
```bash Example request theme={null}
curl -X POST https://api.deepl.com/v2/translate \
--header "Content-Type: application/json" \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--data '{
"text": ["Press Continue to advance."],
"target_lang": "DE",
"tag_handling": "xml",
"tag_handling_version": "v2"
}'
```
```json Example response theme={null}
{
"translations": [
{
"detected_source_language": "EN",
"text": "Drücken Sie „Weiter\", um fortzufahren.",
"tag_handling_version": "v2"
}
]
}
```
Accounts that first used tag handling after December 1, 2025 default to v2. All other accounts default to v1 and need to set `tag_handling_version=v2` explicitly. Results differ between versions, so test representative content before switching versions in production.
With v2, XML input is strictly parsed: invalid XML (for example, an unclosed tag) returns the error `Tag handling parsing failed, please check input.` Make sure your XML is well-formed and handle this error in your integration.
To translate HTML content, see [Translating HTML](/docs/translate/translating-html).
## Translate sentences with inline markup
Send marked-up text as is; tags stay attached to the words they wrap, and placeholder tags are placed next to the translation of the words that precede or follow them:
```markup Request theme={null}
Press Continue to advance to the next page.
```
```markup Response theme={null}
Drücken Sie Weiter , um zur nächsten Seite zu gelangen.
```
```markup Request theme={null}
Please welcome the participants to today's meeting.
```
```markup Response theme={null}
Bitte begrüßen Sie die Teilnehmer des heutigen Treffens.
```
```markup Request theme={null}
The firm said it had been conducting an internal investigation for several months.
```
```markup Response theme={null}
Das Unternehmen sagte, dass es seit mehreren Monaten eine interne Untersuchung durchgeführt habe.
```
```markup Request theme={null}
Artificial intelligence is already shaping our everyday lives.
```
```markup Response theme={null}
Künstliche Intelligenz prägt bereits heute unseren Alltag .
```
## Exclude content from translation
List tags whose content should not be translated in the `ignore_tags` parameter. The example below uses `ignore_tags=x` to preserve the text between `` and ` ` as is:
```text Parameters theme={null}
tag_handling=xml, ignore_tags=x
```
```markup Request theme={null}
Please open the page Settings to configure your system.
```
```markup Response theme={null}
Bitte öffnen Sie die Seite Settings um Ihr System zu konfigurieren.
```
## Translate whole XML documents
Send complete XML files the same way, with `split_sentences=nonewlines` so that line breaks in the file don't split sentences. Tags that contain text (here `title` and `par`) are treated as sentence boundaries, and the content of each is translated separately:
```text Parameters theme={null}
tag_handling=xml, split_sentences=nonewlines
```
```markup Example request theme={null}
A document's title
This is the first sentence. Followed by a second one.
This is the third sentence.
```
```markup Example response theme={null}
Der Titel eines Dokuments
Das ist der erste Satz. Gefolgt von einem zweiten.
Dies ist der dritte Satz.
```
Without `split_sentences=nonewlines`, a newline in the middle of a sentence causes each part to be translated separately, producing wrong results:
```markup Request theme={null}
She bought oat
biscuits.
```
```markup Response theme={null}
Sie kaufte Hafer
Kekse.
```
The two parts of the sentence have been translated separately: "oat biscuits" became "Hafer Kekse" instead of "Haferkekse".
## Keep sentences together across tags
When a single sentence is spread across multiple text-bearing tags, list those tags in the `non_splitting_tags` parameter so the sentence is translated as a whole:
```text Parameters theme={null}
tag_handling=xml, non_splitting_tags=par
```
```markup Request theme={null}
The firm said it had been conducting an internal investigation.
```
```markup Response theme={null}
Die Firma sagte, dass sie eine interne Untersuchung durchgeführt habe .
```
The sentence is translated as a whole and the `par` tags are treated as markup. Because the translation of "had been" moved to another position in the German sentence, the tags are duplicated (which is expected here).
```text Parameters theme={null}
tag_handling=xml
```
```markup Request theme={null}
The firm said it had been conducting an internal investigation.
```
```markup Response theme={null}
Die Firma sagte, es sei eine gute Idee gewesen. Durchführung einer internen Untersuchung.
```
Each `par` element is translated separately, producing an incorrect translation.
## Control sentence splitting manually
If automatic detection of the XML structure doesn't yield good results for your files, turn it off with `outline_detection=0` and list your structure tags in the `splitting_tags` parameter. The example below reproduces the automatic behavior for the document shown earlier:
```text Parameters theme={null}
tag_handling=xml, split_sentences=nonewlines, outline_detection=0, splitting_tags=par,title
```
```markup Example request theme={null}
A document's title
This is the first sentence. Followed by a second one.
This is the third sentence.
```
```markup Example response theme={null}
Der Titel eines Dokuments
Das ist der erste Satz. Gefolgt von einem zweiten.
Dies ist der dritte Satz.
```
This approach takes more setup but gives you full control over how the translation output is structured.
# Understanding Model Types
Source: https://developers.deepl.com/docs/translate/understanding-model-types
How the model_type parameter chooses between latency-optimized and quality-optimized translation models, and how DeepL selects the model for each request.
DeepL hosts many AI models for translation and deploys new ones continuously. Rather than asking you to pick a specific model, and update your integration every time models change, the `model_type` parameter of the [`/v2/translate` endpoint](/api-reference/translate/request-translation) lets you state your goal: the lowest possible latency or the highest possible translation quality. DeepL then chooses the most suitable model for your language pair and request.
## Parameter values
The `model_type` parameter accepts three values:
| Value | Behavior |
| :------------------------- | :----------------------------------------------------------------------------------- |
| `latency_optimized` | Aims to serve the request as fast as possible (default when `model_type` is omitted) |
| `quality_optimized` | Aims for the highest translation quality |
| `prefer_quality_optimized` | Legacy value, currently identical to `quality_optimized` |
All features and language pairs are compatible with all `model_type` values. As of December 2025, all source and target languages are supported by next-gen models.
When you set `model_type`, the response includes a `model_type_used` field indicating which kind of model served the request:
```sh Example request theme={null}
curl -X POST https://api.deepl.com/v2/translate \
--header "Content-Type: application/json" \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--data '{
"text": ["Your order has shipped and will arrive on Tuesday."],
"target_lang": "DE",
"model_type": "quality_optimized"
}'
```
```json Example response theme={null}
{
"translations": [
{
"detected_source_language": "EN",
"text": "Ihre Bestellung wurde versandt und kommt am Dienstag an.",
"model_type_used": "quality_optimized"
}
]
}
```
## How DeepL selects the model
The parameter expresses a goal, not a model name. DeepL fulfills it on a best-effort basis: for some language pairs and requests, only one model can be used, and not every pair behaves differently between the two values. DeepL may also change which model serves a given `model_type` when the change is a net benefit, for example a quality increase with no significant latency cost, or a large latency reduction with at most a very slight quality trade-off.
This means you never need to update your code for new model releases or track model names: the API keeps choosing the best available model for your stated goal.
## Notes
* `model_type` applies to text translation only. The [`/v2/document` endpoint](/api-reference/document/upload-and-translate-a-document) accepts the parameter without error but ignores it.
* The [`/v3/languages` endpoint](/docs/languages/using-the-languages-api) doesn't yet report `model_type` support per language. This information will be added in a future update.
# Write Quickstart
Source: https://developers.deepl.com/docs/translate/write-quickstart
Improve your first text with the DeepL Write API: rephrase for clarity, apply corrections-only mode, and change the writing style.
In this tutorial, you'll improve text with DeepL API for Write: rephrase a text for clarity, run a corrections-only pass that keeps the author's voice intact, and change the writing style. By the end, you'll have used both Write endpoints and know when to pick which.
Unlike translation, Write improves text **within** a language: the source text and `target_lang` must be the same language (improving and translating in one request is not yet supported).
## Prerequisites
* A DeepL API Pro subscription. Write is not yet available on API Free plans, and its use is covered by [these additions to the Service Specification](/api-reference/improve-text/deepl-write-api-service-specification-updates).
* Your API key from [your account settings](https://www.deepl.com/your-account/keys). The same keys work for all DeepL API endpoints, Write included.
* `curl`, or one of the [official client libraries](/docs/getting-started/client-libraries), which support all Write features.
## Building with an AI coding agent?
Wire it up to the [DeepL Docs MCP Server](/docs/getting-started/docs-mcp-server) so it can search and read this documentation while it writes code. In Claude Code:
```bash theme={null}
claude mcp add --transport http deepl-docs https://developers.deepl.com/mcp
```
Then describe what you want to build. To get the same result as this tutorial, paste:
```text wrap theme={null}
Using the DeepL Write API, write a script that rephrases an English text, then runs the same text through corrections-only mode, and prints both results.
```
Setup instructions for Claude Desktop, Cursor, VS Code, and other MCP clients are on the [Docs MCP Server page](/docs/getting-started/docs-mcp-server).
## Step 1: Rephrase a text
The [`/v2/write/rephrase` endpoint](/api-reference/improve-text/request-text-improvement) is Write's broad improvement mode: it fixes spelling and grammar, and may also rewrite sentences for clarity, style, or tone.
```sh Sample request theme={null}
export API_KEY={YOUR_API_KEY}
curl -X POST https://api.deepl.com/v2/write/rephrase \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"text": [
"I could relly use sum help with edits on thiss text !"
],
"target_lang": "en-US"
}'
```
```json Sample response theme={null}
{
"improvements": [
{
"text": "I could really use some help editing this text!",
"detected_source_language": "en",
"target_language": "en-US"
}
]
}
```
The `text` parameter is an array, so you can improve multiple texts in one request; improvements come back in the same order. The total request body is limited to 10 KiB, so split larger workloads across multiple calls.
`target_lang` currently supports `de`, `en-GB`, `en-US`, `es`, `fr`, `it`, `ja`, `ko`, `pt-BR`, `pt-PT`, and `zh`/`zh-Hans`. To check programmatically, call [`GET /v3/languages?resource=write`](/docs/languages/using-the-languages-api).
You can convert between variants of the same language: sending American English text with `target_lang` set to `en-GB` improves the text and converts it to British English.
## Step 2: Fix errors only, keeping the author's voice
When you want corrections without rewrites, use the [`/v2/write/correct` endpoint](/api-reference/improve-text/correct-text) instead. It fixes spelling and grammar with minimal changes to wording, matching the "Corrections Only" mode in the DeepL Translator UI.
```sh Sample request theme={null}
curl -X POST https://api.deepl.com/v2/write/correct \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"text": [
"I could relly use sum help with edits on thiss text !"
],
"target_lang": "en-US"
}'
```
```json Sample response theme={null}
{
"improvements": [
{
"text": "I could really use some help with edits on this text!",
"detected_source_language": "en",
"target_language": "en-US"
}
]
}
```
Compare the two results: `/write/correct` fixed the errors but kept the original phrasing ("edits on this text"), while `/write/rephrase` also reworded the sentence.
## Step 3: Change the writing style
On `/write/rephrase`, the `writing_style` parameter steers how the text is rewritten:
```sh Sample request theme={null}
curl -X POST https://api.deepl.com/v2/write/rephrase \
--header "Authorization: DeepL-Auth-Key $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"text": [
"I could relly use sum help with edits on thiss text !"
],
"target_lang": "en-US",
"writing_style": "business"
}'
```
```json Sample response theme={null}
{
"improvements": [
{
"text": "I would appreciate some assistance with editing this text.",
"detected_source_language": "en",
"target_language": "en-US"
}
]
}
```
Alternatively, the `tone` parameter adjusts how the text sounds (friendly, confident, diplomatic, and more). A request can include `writing_style` or `tone`, but not both. For all available values, per-language support, and the `prefer_` fallback behavior, see [Controlling Writing Style and Tone](/docs/translate/controlling-writing-style-and-tone).
## Next steps
You've now used both Write endpoints and steered the output style. To keep going:
* Explore all style and tone options in [Controlling Writing Style and Tone](/docs/translate/controlling-writing-style-and-tone)
* See the full request and response schemas in the [`/write/rephrase`](/api-reference/improve-text/request-text-improvement) and [`/write/correct`](/api-reference/improve-text/correct-text) references
* Rephrase between language variants with the [language variants guide](/docs/learning-how-tos/examples-and-guides/translating-between-variants)
* Note that Write characters count toward the same [usage quota and cost control limits](/docs/resources/usage-limits) as translation characters
# Message Encoding
Source: https://developers.deepl.com/docs/voice/message-encoding
Choose between JSON and MessagePack encoding for DeepL Voice API WebSocket messages, and avoid the frame type and map encoding pitfalls.
WebSocket messages in a Voice API session can be encoded in two formats, chosen with the `message_format` option when you [request the session](/api-reference/voice/request-session). Start with JSON for the best developer experience, and switch to MessagePack if you need better performance.
| **Format** | **Frame type** | **Binary data** | **Trade-off** |
| :---------------------------------- | :------------- | :--------------------- | :------------------------------------------------------------ |
| JSON (default) | TEXT | base64-encoded strings | Human-readable, easy to debug |
| [MessagePack](https://msgpack.org/) | BINARY | raw binary | Roughly 25-30% less bandwidth, 2x-4x faster encoding/decoding |
Send JSON messages as TEXT frames and MessagePack messages as BINARY frames. Sending the wrong frame type results in connection errors.
MessagePack messages must be encoded as maps with string keys, not as arrays. The structure must match the JSON schema exactly, with all field names preserved (for example, `{"source_media_chunk": {"data": }}`). Some MessagePack libraries default to array encoding for performance, so check your library's configuration.
The following example sends the same audio chunk in both encodings:
```javascript JSON theme={null}
// Raw binary audio data
const audioData = getAudioChunk();
// Base64 encode the audio data
const base64Audio = btoa(audioData);
const message = {
source_media_chunk: {
data: base64Audio
}
};
// Send as TEXT frame
websocket.send(JSON.stringify(message));
```
```javascript MessagePack theme={null}
import { pack } from 'msgpackr';
// Raw binary audio data
const audioData = getAudioChunk();
const message = {
source_media_chunk: {
data: audioData // No base64 encoding needed
}
};
// Send as BINARY frame
websocket.send(pack(message));
```
For the full list of message types exchanged during a session, see the [WebSocket Streaming reference](/api-reference/voice/websocket-streaming). For how those messages fit into the session lifecycle, see [Understanding Voice Sessions](/docs/voice/understanding-voice-sessions).
# DeepL Voice API
Source: https://developers.deepl.com/docs/voice/overview
Transcribe and translate spoken audio in real time with the DeepL Voice API. Find the streaming guide, core concepts, and language and format reference.
The DeepL Voice API transcribes and translates spoken audio in real time over a WebSocket connection. Within a single streaming session, you can:
* Send one audio stream
* Receive transcripts in the source language
* Receive translations in multiple target languages
* Receive translated speech
**Speech-to-text** (real-time transcription and text translation) and **speech-to-speech** (translated TTS output) are available to all customers with a paid DeepL API subscription.
The provisions applying to DeepL API Enterprise subscriptions also apply to Voice API speech-to-text, with [additions to the Terms and Conditions, Service Specification, and Data Processing Agreement](/api-reference/voice/deepl-voice-api-service-specification-updates) (new sub-processors have been added to serve specific languages).
## Start here
Create a session, stream audio over WebSocket, and handle reconnections, with a complete Python example.
Understand the session flow, token lifecycle, and how audio and results are delivered.
Choose between JSON and MessagePack for WebSocket messages.
Check transcription, translation, and translated speech availability per language.
Check supported audio codecs and containers, chunk sizes, and session limits.
Full request, message, and response schemas for the Voice API endpoints.
## Customization
Two optional features let you tailor transcription and translation to your domain:
* **Spoken terms**: improve transcription of frequently used terms such as company-specific terminology, acronyms, product names, and team member names. Manage them in [DeepL Home](https://www.deepl.com/en/voice/spoken-terms) or via the API; see [Improving Transcription with Spoken Terms](/docs/customize/improving-transcription-with-spoken-terms).
* **Glossaries**: enforce specific translations for terms in the target language. A session can apply several glossaries in priority order; see [Glossaries in realtime Voice](#glossaries-in-realtime-voice) below and the [Request Session reference](/api-reference/voice/request-session). Manage glossaries in [DeepL Home](https://www.deepl.com/en/glossary) or programmatically with the [Glossaries API](/docs/customize/managing-glossaries).
### Glossaries in realtime Voice
Because Voice translates in real time, glossary terms are matched against the source transcription as it's produced, not against a complete text as in batch text translation. The transcription arrives incrementally, so a glossary term is applied only when its full source term appears in the streamed transcription.
This matters most for multi-word glossary terms. Since the source is transcribed piece by piece, a multi-word term is matched only when its words are transcribed together. In most cases they are, and the term is applied, but a term whose words are concluded across separate transcription segments can occasionally be missed, and the longer the term, the higher that chance. Single-word terms aren't affected in the same way. This is inherent to streaming transcription, not a temporary limitation.
For languages written without spaces between words, such as Japanese, Chinese, and Thai, the transcription also determines where each term begins and ends, so a glossary's source term must correspond to what the transcription produces.
Each glossary must contain a dictionary for the session's source and target language pair. If the source language is detected rather than fixed, and it resolves to a language a glossary has no dictionary for, that glossary isn't applied.
## Code examples
A reference implementation in Python is available in the [DeepL Python library repository](https://github.com/DeepL/deepl-python/tree/main/examples/voice/cli). The official DeepL SDKs don't integrate the Voice API yet, but you can use any WebSocket client library to interact with it.
# Real-Time Voice Quickstart
Source: https://developers.deepl.com/docs/voice/real-time-voice-quickstart
Stream microphone audio to the DeepL Voice API from Python and print live translations to your terminal as the speaker talks.
In this tutorial you'll run a small Python program that captures audio from your microphone, streams it to the DeepL Voice API, and prints the transcript plus German and French translations to your terminal, sentence by sentence, while you speak. The same pattern works for any live audio source: a meeting bot, a phone bridge, or a broadcast feed.
## Prerequisites
* A DeepL API account with Voice API access
* Python 3.10 or later
* A microphone (no microphone? see [Simulate a live stream from a file](#simulate-a-live-stream-from-a-file))
## Building with an AI coding agent?
Wire it up to the [DeepL Docs MCP Server](/docs/getting-started/docs-mcp-server) so it can search and read this documentation while it writes code. In Claude Code:
```bash theme={null}
claude mcp add --transport http deepl-docs https://developers.deepl.com/mcp
```
Then describe what you want to build. To get the same result as this tutorial, paste:
```text wrap theme={null}
Write a script to stream my microphone to the DeepL Voice API and print the transcript and translations as each sentence concludes.
```
Setup instructions for Claude Desktop, Cursor, VS Code, and other MCP clients are on the [Docs MCP Server page](/docs/getting-started/docs-mcp-server).
## Run the complete example
Install the dependencies:
```bash theme={null}
pip install requests sounddevice websockets
```
Then save this as `live_translation.py` and replace `YOUR_AUTH_KEY` with your DeepL API key:
```python live_translation.py [expandable] theme={null}
import asyncio
import base64
import json
import signal
import requests
import sounddevice
import websockets
AUTH_KEY = "YOUR_AUTH_KEY"
SESSION_ENDPOINT = "https://api.deepl.com/v3/voice/realtime"
TARGET_LANGUAGES = ["de", "fr"]
SAMPLE_RATE = 16000 # Must match the rate declared in source_media_content_type
CHUNK_FRAMES = 3200 # 200 ms per chunk at 16 kHz
RECORD_SECONDS = 30 # Safety cap; Ctrl+C stops recording earlier
def create_session() -> dict:
response = requests.post(
SESSION_ENDPOINT,
headers={"Authorization": f"DeepL-Auth-Key {AUTH_KEY}"},
json={
"source_media_content_type": "audio/pcm;encoding=s16le;rate=16000",
"target_languages": TARGET_LANGUAGES,
},
)
response.raise_for_status()
return response.json()
async def send_microphone_audio(ws, stop: asyncio.Event) -> None:
stream = sounddevice.RawInputStream(
samplerate=SAMPLE_RATE, channels=1, dtype="int16"
)
stream.start()
try:
for _ in range(RECORD_SECONDS * SAMPLE_RATE // CHUNK_FRAMES):
if stop.is_set():
break
# Read in a worker thread so receiving continues while we block
data, _overflowed = await asyncio.to_thread(stream.read, CHUNK_FRAMES)
encoded = base64.b64encode(bytes(data)).decode("ascii")
await ws.send(json.dumps({"source_media_chunk": {"data": encoded}}))
finally:
stream.stop()
stream.close()
print("Finalizing transcripts...")
await ws.send(json.dumps({"end_of_source_media": {}}))
async def receive_results(ws) -> None:
pending = {} # Concluded text per language, buffered until a sentence ends
def handle_update(label: str, segments: list) -> None:
text = pending.get(label, "") + "".join(s["text"] for s in segments)
if text.rstrip().endswith((".", "!", "?")):
print(f"[{label}] {text.strip()}")
text = ""
pending[label] = text
async for message in ws:
data = json.loads(message)
if "source_transcript_update" in data:
handle_update("source", data["source_transcript_update"]["concluded"])
elif "target_transcript_update" in data:
update = data["target_transcript_update"]
handle_update(update["language"], update["concluded"])
elif "end_of_stream" in data:
# The very last message: flush any unfinished sentences and exit
for label, text in pending.items():
if text.strip():
print(f"[{label}] {text.strip()}")
return
elif "error" in data:
raise RuntimeError(f"Voice API error: {data['error']['error_message']}")
async def main() -> None:
stop = asyncio.Event()
try:
asyncio.get_running_loop().add_signal_handler(signal.SIGINT, stop.set)
except NotImplementedError:
pass # Windows: Ctrl+C raises KeyboardInterrupt instead
session = create_session()
url = f"{session['streaming_url']}?token={session['token']}"
async with websockets.connect(url) as ws:
print("Connected. Speak now (Ctrl+C to stop)...")
await asyncio.gather(send_microphone_audio(ws, stop), receive_results(ws))
print("Done. All transcripts are final.")
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
pass
```
Run it and speak in any [supported source language](/docs/voice/supported-voice-languages). Each sentence prints once it's final, first the transcript, then each translation. Press Ctrl+C when you're done: the script stops recording, waits for the remaining results, and exits cleanly.
```text theme={null}
❯ python live_translation.py
Connected. Speak now (Ctrl+C to stop)...
[source] Hello everyone, welcome to today's demo.
[de] Hallo zusammen, willkommen zur heutigen Demo.
[fr] Bonjour à tous, bienvenue à la démonstration d'aujourd'hui.
^CFinalizing transcripts...
[source] We are testing real-time voice translation.
[de] Wir testen die Sprachübersetzung in Echtzeit.
[fr] Nous testons la traduction vocale en temps réel.
Done. All transcripts are final.
```
## How it works
The program does three things: it creates a session over HTTPS, streams audio to the WebSocket URL it gets back, and handles result messages until the server confirms everything is final.
### The session request
`create_session` sends a POST request with the audio format and target languages. The only required field is `source_media_content_type`; for raw microphone audio, that's PCM at 16 kHz. The response contains the WebSocket URL and a one-time token, and the program connects to `{streaming_url}?token={token}`:
```json theme={null}
{
"session_id": "4f911080-cfe2-41d4-8269-0e6ec15a0354",
"streaming_url": "wss://api.deepl.com/v3/voice/realtime/connect",
"token": "VGhpcyBpcyBhIGZha2UgdG9rZW4K"
}
```
If you know the speaker's language in advance, add `"source_language": "en"` and `"source_language_mode": "fixed"` to the request body. This skips language detection and reduces latency. Without them, the language is detected automatically. See the [Request Session reference](/api-reference/voice/request-session) for all parameters, including glossary and formality options.
### Sending audio
`send_microphone_audio` sends each 200-millisecond microphone chunk as a JSON text message with the raw audio base64-encoded:
```json theme={null}
{"source_media_chunk": {"data": ""}}
```
Keep chunks between 50 and 250 milliseconds for the best latency. When the audio ends (Ctrl+C or the safety cap), the function sends one final message that tells the API to finalize all pending results:
```json theme={null}
{"end_of_source_media": {}}
```
### Receiving transcripts and translations
While audio is being sent, the server pushes `source_transcript_update` and `target_transcript_update` messages on the same connection:
```json theme={null}
{
"target_transcript_update": {
"language": "de",
"concluded": [
{"text": " Hallo zusammen,", "start_time": 0, "end_time": 1500}
],
"tentative": [
{"text": " willkommen zur heutigen Demo", "start_time": 1500, "end_time": 2000}
]
}
}
```
**Concluded** segments are final and sent only once; **tentative** segments are provisional and refined by later updates. `receive_results` appends concluded text to a per-language buffer and prints the buffer whenever a sentence completes, which keeps the terminal readable. A UI would also render the tentative text as a live preview that updates in place; see [Understanding Voice Sessions](/docs/voice/understanding-voice-sessions) for this delivery model.
After `end_of_source_media`, the server sends the remaining updates, then `end_of_stream` as the very last message, at which point it's safe to close the connection. See the [WebSocket Streaming reference](/api-reference/voice/websocket-streaming) for the full message schema.
### Reconnecting after a drop
Networks drop, and the Voice API lets you resume a session instead of starting over. If the WebSocket closes unexpectedly, exchange your token for a fresh streaming URL and token, then reconnect. Session state, including configuration and translation context, is preserved.
```python theme={null}
def reconnect(token: str) -> dict:
response = requests.get(
SESSION_ENDPOINT,
headers={"Authorization": f"DeepL-Auth-Key {AUTH_KEY}"},
params={"token": token},
)
response.raise_for_status()
return response.json()
```
The response has the same shape as the session response above. Always pass the token from your most recent session or reconnection response: each token is single-use, and presenting an outdated one invalidates the session (a 400-level error). In that case, create a new session with `create_session()`.
To add this to the example, wrap the `websockets.connect` block in a `try`/`except websockets.exceptions.ConnectionClosed` loop that calls `reconnect()` and connects again with the new URL and token.
## Simulate a live stream from a file
If you don't have a microphone, or you want reproducible input while developing, stream a pre-recorded file at real-time pace instead. Set `"source_media_content_type": "audio/auto"` in the session request so the format is detected, and swap `send_microphone_audio` for a reader that paces itself:
```python theme={null}
async def send_file_audio(ws, path: str, stop: asyncio.Event) -> None:
with open(path, "rb") as audio_file:
while chunk := audio_file.read(6400):
if stop.is_set():
break
encoded = base64.b64encode(chunk).decode("ascii")
await ws.send(json.dumps({"source_media_chunk": {"data": encoded}}))
# Pace the upload to simulate live audio
await asyncio.sleep(0.2)
print("Finalizing transcripts...")
await ws.send(json.dumps({"end_of_source_media": {}}))
```
Any recording of speech in a [supported format](/docs/voice/voice-api-requirements#supported-audio-formats) works, for example an MP3 of a podcast episode.
Don't send audio faster than 2x real-time. Uploading a file as fast as the network allows triggers rate limits and terminates the session.
## Next steps
* See the [WebSocket Streaming reference](/api-reference/voice/websocket-streaming) for the complete message schema and all event types.
* To understand the session flow and token lifecycle in depth, see [Understanding Voice Sessions](/docs/voice/understanding-voice-sessions).
* To reduce bandwidth with MessagePack instead of JSON, see [Message Encoding](/docs/voice/message-encoding).
* For a fuller command-line version of this program, including glossary and formality options, see the [DeepL Voice CLI example](https://github.com/DeepL/deepl-python/tree/main/examples/voice/cli).
* To apply a custom glossary to your translations, see the [Glossaries API](/docs/customize/managing-glossaries).
# Supported Voice Languages
Source: https://developers.deepl.com/docs/voice/supported-voice-languages
Language availability for the DeepL Voice API: transcription, translation, and translated speech support per language.
Translation is always provided by DeepL. For some languages, transcription and translated speech are provided by external service partners. All source languages can be translated into any target language.
| **Language** | **Transcription** | **Translation** | **Translated Speech** |
| :------------------------------- | :---------------: | :-------------: | :-------------------: |
| Arabic | ⎋ | ✓ | ⎋ |
| Bengali | ⎋ | ✓ | — |
| Bulgarian | ⎋ | ✓ | ⎋ |
| Chinese (Simplified/Traditional) | ✓ | ✓ | ✓ |
| Croatian | ⎋ | ✓ | ⎋ |
| Czech | ✓ | ✓ | ⎋ |
| Danish | ⎋ | ✓ | ⎋ |
| Dutch | ✓ | ✓ | ✓ |
| English (American/British) | ✓ | ✓ | ✓ |
| Estonian | ⎋ | ✓ | — |
| Finnish | ⎋ | ✓ | ⎋ |
| French | ✓ | ✓ | ✓ |
| German | ✓ | ✓ | ✓ |
| Greek | ⎋ | ✓ | ⎋ |
| Hebrew | ⎋ | ✓ | — |
| Hindi beta | ⎋ | ✓ | ⎋ |
| Hungarian | ⎋ | ✓ | ⎋ |
| Indonesian | ✓ | ✓ | ⎋ |
| Irish | ⎋ | ✓ | — |
| Italian | ✓ | ✓ | ✓ |
| Japanese | ✓ | ✓ | ✓ |
| Korean | ✓ | ✓ | ✓ |
| Latvian | ⎋ | ✓ | — |
| Lithuanian | ⎋ | ✓ | — |
| Malay beta | ⎋ | ✓ | ⎋ |
| Maltese | ⎋ | ✓ | — |
| Norwegian (bokmål) | ⎋ | ✓ | ⎋ |
| Polish | ✓ | ✓ | ✓ |
| Portuguese (Brazil/Portugal) | ✓ | ✓ | ✓ |
| Romanian | ✓ | ✓ | ⎋ |
| Russian | ✓ | ✓ | ✓ |
| Slovak | ⎋ | ✓ | ⎋ |
| Slovenian | ⎋ | ✓ | — |
| Spanish | ✓ | ✓ | ✓ |
| Swedish | ✓ | ✓ | ✓ |
| Tagalog | ⎋ | ✓ | ⎋ |
| Tamil beta | ⎋ | ✓ | ⎋ |
| Thai | ⎋ | ✓ | — |
| Turkish | ✓ | ✓ | ✓ |
| Ukrainian | ✓ | ✓ | ⎋ |
| Vietnamese | ⎋ | ✓ | ⎋ |
✓ provided by DeepL / ⎋ provided by an external service partner / — not available
Transcription provided by external service partners (marked with ⎋) cannot yet auto-detect the source language,
which you must therefore specify explicitly.
To retrieve supported languages and feature availability programmatically, call [`GET /v3/languages?resource=voice`](/docs/languages/using-the-languages-api) and check for the `transcription` and `translated_speech` feature keys. The `external` flag on these features indicates if they are provided by an external service partner.
For audio format support and session limits, see [Voice API Requirements](/docs/voice/voice-api-requirements).
# Understanding Voice Sessions
Source: https://developers.deepl.com/docs/voice/understanding-voice-sessions
Understand the lifecycle of a DeepL Voice API session: how connections are established, how tokens secure reconnection, and how audio and results flow.
This page explains the lifecycle of a Voice API session: how a connection is established, how tokens keep it secure and resumable, and how audio and results flow over the WebSocket. To run this flow end to end first, see the [Real-Time Voice Quickstart](/docs/voice/real-time-voice-quickstart).
## The two-step connection flow
Every Voice API session starts with two steps:
1. [Request a session](/api-reference/voice/request-session) with a POST request to `/v3/voice/realtime`. This is where authentication happens and where you fix the session's configuration: audio formats, source and target languages, glossaries, spoken terms, [message encoding](/docs/voice/message-encoding), and an optional [custom reporting tag](/docs/learning-how-tos/examples-and-guides/how-to-use-custom-reporting-tags). The response contains an ephemeral streaming URL and a token, both valid for one-time use.
2. [Open a WebSocket connection](/api-reference/voice/websocket-streaming) to the streaming URL, passing the token as a query parameter. All audio and results are exchanged as messages over this connection.
Splitting setup from streaming keeps your API key out of the WebSocket handshake and settles all configuration before any audio flows. The WebSocket itself carries only audio and results.
```mermaid theme={null}
sequenceDiagram
participant Client
participant Voice API
Note over Client,Voice API: Step 1: Request Session (POST)
Client->>Voice API: Configuration options
Voice API->>Client: Streaming URL and token
Note over Client,Voice API: Step 2: Start Streaming (WebSocket)
Client->>Voice API: Establish WebSocket connection using the streaming URL
Note over Client,Voice API: WebSocket Connection Established
Client<<->>Voice API: Bidirectional message exchange: Send audio, receive transcripts, translations, and speech
Note over Client,Voice API: Stream Closed
```
## Tokens and reconnection
Network connections are unreliable, so the Voice API is built around resumable sessions. A session is identified and secured by its token, which rotates over its lifetime: the initial session response and every reconnection response contain a new token, and your client should always keep the most recent one.
If the connection drops, you exchange the latest token for a new streaming URL and token via [GET `/v3/voice/realtime`](/api-reference/voice/reconnect-session), then reconnect and pick up where you left off. Session state, including configuration and translation context, is preserved across reconnections.
Two security properties follow from the token design:
* Each token and streaming URL is valid for one-time use. Using a token more than once to open a WebSocket connection terminates the session immediately.
* Only the latest token can request a reconnection. Presenting an outdated token invalidates the session.
Requesting a reconnection token while a connection is still active disconnects that connection, so only reconnect after the existing connection has closed. Connections also have a maximum duration; when it's reached, reconnect the same way to continue the session. See [session limits](/docs/voice/voice-api-requirements#session-limits) for the exact values.
## How audio and results flow
Once connected, you send audio continuously as [source media chunk](/api-reference/voice/websocket-streaming) messages. Smaller chunks mean lower latency, because the API can start processing sooner. When the audio ends, an [end of source media](/api-reference/voice/websocket-streaming) message tells the API to finalize all pending results. Keep audio flowing: a session with no incoming data times out and is terminated.
As audio is processed, transcripts and translations arrive incrementally via [source transcript updates](/api-reference/voice/websocket-streaming) and [target transcript updates](/api-reference/voice/websocket-streaming). Each update distinguishes two kinds of segments:
* **Concluded segments**: finalized text that will not change. These are sent once and remain fixed.
* **Tentative segments**: preliminary text that may be refined as more audio context becomes available.
Applications typically append concluded segments to the running transcript and display tentative segments as provisional text that gets replaced by later updates.
### Translated speech
When a translated speech target is configured, synthesized audio arrives incrementally as [target media chunks](/api-reference/voice/websocket-streaming). To save bandwidth, the stream contains only speech, without silence or padding, so there are gaps with no data whenever the speaker pauses. Each chunk carries text and audio duration information, which you can use to highlight the currently spoken text or to subtitle the audio output.
```mermaid theme={null}
sequenceDiagram
participant Client
participant Voice API
Note over Client,Voice API: WebSocket Connection Established
par
loop Send audio data
Client->>Voice API: source_media_chunk
end
and
loop Receive updates
Voice API-->>Client: source_transcript_update
end
and Per target language
loop Receive updates
Voice API-->>Client: target_transcript_update
end
and Per target language
loop Receive translated speech
Voice API-->>Client: target_media_chunk
end
end
Client->>Voice API: end_of_source_media
par
loop Final updates
Voice API-->>Client: source_transcript_update
end
and Per target language
loop Final updates
Voice API-->>Client: target_transcript_update
end
and Per target language
loop Final audio chunks
Voice API-->>Client: target_media_chunk
end
end
Voice API-->>Client: end_of_source_transcript
Voice API-->>Client: end_of_target_transcript (once per target language)
Voice API-->>Client: end_of_target_media (once per target language)
Voice API-->>Client: end_of_stream
Note over Client,Voice API: Stream Closed
```
`par` means parallel execution and `loop` means looped execution.
# Voice API Requirements
Source: https://developers.deepl.com/docs/voice/voice-api-requirements
Audio format requirements and session limits for the DeepL Voice API: supported codecs and containers, chunk sizes, and connection rules.
## Supported audio formats
The API supports common combinations of streaming codecs and containers with a single-channel (mono) audio stream.
| **Audio Codec** | **Audio Container** | **Recommended Bitrate** |
| :---------------------------- | :---------------------------------- | :--------------------------------------------------- |
| **PCM** | **-** | **256 kbps (16kHz), default recommendation** |
| **OPUS** | **Matroska / MPEG-TS / Ogg / WebM** | **32 kbps, recommended for low bandwidth scenarios** |
| AAC | Matroska / MPEG-TS | 96 kbps |
| FLAC | FLAC / Matroska / Ogg | 256 kbps (16kHz) |
| MP3 | MPEG / Matroska | 128 kbps |
For the detailed list of supported input audio formats, see [Source Media Content Type](/api-reference/voice/request-session#body-source-media-content-type). For supported output audio formats, see [Target Media Content Type](/api-reference/voice/request-session#body-target-media-content-type).
## Session limits
* Maximum 5 translation targets per session (including translated speech targets)
* Maximum 1 translated speech target per session
* Maximum 10 glossaries per session ([`glossary_ids`](/api-reference/voice/request-session), ordered by priority)
* Audio chunk size: should not exceed 100 kilobytes or 1 second duration
* Recommended chunk duration: 50-250 milliseconds for low latency
* Audio stream speed: maximum 2x real-time
* Timeout: if no data is received for 30 seconds, the session is terminated
* Maximum connection duration: after 1 hour, the connection is closed. Establish a new connection by [reconnecting](/api-reference/voice/reconnect-session) to the session
* Using any given token more than once to establish a WebSocket connection terminates the associated session immediately for security reasons
If you need more translation targets or translated speech targets than these limits allow, open multiple concurrent sessions over the same source audio.
For language availability, see [Supported Voice Languages](/docs/voice/supported-voice-languages).