Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@
"docs/resources/privacy"
]
}
],
"pages": [
"docs/languages/query-supported-languages-for-a-resource"
]
},
{
Expand Down Expand Up @@ -157,7 +160,8 @@
"docs/voice/understanding-voice-sessions",
"docs/voice/message-encoding",
"docs/voice/supported-voice-languages",
"docs/voice/voice-api-requirements"
"docs/voice/voice-api-requirements",
"docs/voice/translate-a-pre-recorded-audio-file"
]
},
{
Expand Down
173 changes: 173 additions & 0 deletions docs/languages/query-supported-languages-for-a-resource.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
---
title: "Query supported languages for a resource"

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Frontmatter description could be more specific about the outcome

The description 'fetch which languages and features a specific DeepL API resource supports, then filter and use that data in your integration' is accurate but somewhat generic on the second half. It would be clearer if it named the concrete outcomes (source/target separation, feature detection).

Suggested change
title: "Query supported languages for a resource"
title: "Query supported languages for a resource"
description: "Use GET /v3/languages to fetch supported languages for a DeepL resource, then separate source and target languages and check feature support before making translation requests."

description: "Use GET /v3/languages to fetch which languages and features a specific DeepL API resource supports, then filter and use that data in your integration."
covers: [Languages]
---

The `/v3/languages` endpoint tells you exactly which languages a given DeepL API resource supports, whether each language can be used as a source, target, or both, and which optional features (formality, glossaries, tag handling) are available per language.

This guide shows you how to query languages for a specific resource and use the response to drive language validation and feature detection in your integration.

<Info>
The `/v3/languages` endpoint replaces the deprecated `/v2/languages` endpoint. If you're currently using `/v2/languages`, see the [migration guide](/docs/languages/migrating-from-v2-languages) for differences and code examples.
</Info>

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Info box references a migration guide that may not exist

The <Info> box links to /docs/languages/migrating-from-v2-languages. If this page does not exist, the link is broken. The 'Next steps' section also links to it. Verify both links resolve.

Suggested change
</Info>
</Info>
> **Note:** A migration guide for `/v2/languages` to `/v3/languages` is forthcoming. In the meantime, refer to the API changelog for differences.


## Prerequisites

- A DeepL API key. Find yours on your [account page](https://www.deepl.com/your-account/keys).
- If you're on the free plan, replace `https://api.deepl.com` with `https://api-free.deepl.com` in all examples below.

## Step 1: Choose a resource

The `resource` parameter is required. It tells the endpoint which DeepL API product you're querying language support for:

| **Value** | **Product** |
|---|---|
| `translate_text` | Text translation (`/v2/translate`) |
| `translate_document` | Document translation (`/v2/document`) |
| `voice` | Speech transcription and translation (`/v3/voice`) |
| `write` | Text improvement (`/v2/write`) |
| `glossary` | Glossary management |
| `style_rules` | Style rules |
| `translation_memory` | Translation memories |

For this guide, we'll use `translate_text`.

## Step 2: Fetch the language list

Make a `GET` request to `/v3/languages` with your chosen resource:

```sh
curl -X GET 'https://api.deepl.com/v3/languages?resource=translate_text' \
--header 'Authorization: DeepL-Auth-Key YOUR_API_KEY'
```

The response is a JSON array. Each entry describes one language and its capabilities for the requested resource:

Example response (truncated):

```json
[
{
"lang": "de",
"name": "German",
"status": "stable",
"usable_as_source": true,
"usable_as_target": true,
"features": {
"formality": { "status": "stable" },
"glossary": { "status": "stable" },
"tag_handling": { "status": "stable" }
}
},
{
"lang": "en",
"name": "English",
"status": "stable",
"usable_as_source": true,
"usable_as_target": false,
"features": {
"glossary": { "status": "stable" },
"tag_handling": { "status": "stable" }
}
},
{
"lang": "en-US",
"name": "English (American)",
"status": "stable",
"usable_as_source": false,
"usable_as_target": true,
"features": {
"glossary": { "status": "stable" },
"tag_handling": { "status": "stable" }
}
}
]

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Step 3 output does not show example console output

The Python snippet prints to stdout but no expected output is shown. Other steps show responses; consistency suggests showing the expected print output here (e.g. 'Source languages: 31 / Target languages: 33') so readers can verify they're on track.

Suggested fix: Add a comment block or a separate code block after the snippet showing expected output, e.g.: # Source languages: 31\n# Target languages: 33

```

Notice that `en` and `en-US` are separate entries: `en` is source-only, and `en-US` is target-only. Some languages have regional or script variants like this — always check `usable_as_source` and `usable_as_target` rather than assuming a language code is valid in both roles.

<Warning>
Do not hardcode assumptions about language code format. Codes follow BCP 47 and can vary significantly — `de`, `en-US`, `zh-Hans` are all valid. Treat language codes as opaque identifiers and store them as strings. See the [language release process](/docs/resources/language-release-process) for details.
</Warning>

## Step 3: Separate source and target languages

Filter the response by `usable_as_source` and `usable_as_target` to build your source and target language lists:

```python
import urllib.request
import urllib.parse
import json

API_KEY = "YOUR_API_KEY"

# Fetch languages for text translation
url = "https://api.deepl.com/v3/languages?resource=translate_text"
req = urllib.request.Request(
url,
headers={"Authorization": f"DeepL-Auth-Key {API_KEY}"}
)
with urllib.request.urlopen(req) as response:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Step 4 comment logic for 'all other features' is stated as a rule but may be inaccurate

The comment 'All other features (glossary, tag_handling) require both languages to support them' asserts a product rule inline. If this rule is wrong or changes, the code silently misbehaves. The guide says to see the API reference for full details, but the comment presents it as definitive. Either remove the assertion and defer to the reference, or cite the reference inline.

Suggested fix: Change the comment to: '# For glossary and tag_handling, check both languages — see the API reference for the exact rules per resource.' and remove the implied guarantee that this covers all cases.

languages = json.loads(response.read())

# Separate source and target languages
source_languages = [lang for lang in languages if lang["usable_as_source"]]
target_languages = [lang for lang in languages if lang["usable_as_target"]]

print(f"Source languages: {len(source_languages)}")
print(f"Target languages: {len(target_languages)}")

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Step 4 function does not handle missing lang_by_code keys

The example calls lang_by_code.get('en') and lang_by_code.get('de'), which can return None. Passing None to feature_supported() will raise an AttributeError on .get('features', {}). The guide doesn't warn about this or show defensive handling, so a reader copy-pasting into production code could hit a silent bug.

Suggested fix: Add a guard before the call, e.g. if source is None or target is None: raise ValueError(...), or add a note that the caller must validate the codes exist in lang_by_code before calling the function.

# Build a lookup by language code
lang_by_code = {lang["lang"]: lang for lang in languages}
```

## Step 4: Check feature support for a language pair

Before making a translation request with an optional feature (formality, glossary, tag handling), check whether the relevant languages support it. Each language entry includes a `features` object listing which optional features it supports. Check this object for both source and target languages before using a feature in a request. For full details on which languages must support a feature for each resource, see the [API reference for `GET /v3/languages`](/api-reference/languages/retrieve-languages-by-resource).

The `features` dict on each language entry from Step 3 contains exactly the features that language supports — you can check this directly without any additional API calls:

```python
# Requires lang_by_code from Step 3

def feature_supported(source_lang, target_lang, feature_name):
"""Return True if the feature is available for the given language pair."""
# Formality only needs to be supported by the target language,
# because it affects how the output is phrased, not how the source is parsed.
if feature_name == "formality":
return feature_name in target_lang.get("features", {})

# All other features (glossary, tag_handling) require both languages to support them.
source_ok = feature_name in source_lang.get("features", {})
target_ok = feature_name in target_lang.get("features", {})
return source_ok and target_ok


# Example: check if formality is supported for English → German

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Step 5 lacks an example response

All other steps that make an API call (Steps 2, 3) show a response. Step 5 adds the include=beta parameter but shows only the request. Per CLAUDE.md, API requests should be paired with sample responses.

Suggested fix: Add a truncated example JSON response showing at least one language entry with "status": "beta" and one feature with "status": "beta" to illustrate what the beta output looks like.

source = lang_by_code.get("en")
target = lang_by_code.get("de")

print(feature_supported(source, target, "formality"))
# True — German supports formality as a target language

print(feature_supported(source, target, "glossary"))
# True — English and German both support glossary
```

## Step 5: Include beta languages (optional)

By default, the endpoint returns only `stable` languages. To include `beta` languages, add `include=beta`:

```sh
curl -X GET 'https://api.deepl.com/v3/languages?resource=translate_text&include=beta' \
--header 'Authorization: DeepL-Auth-Key YOUR_API_KEY'
```

Each language and each feature entry carries a `status` field: `stable`, `beta`, or `early_access`. If you include beta languages, filter on this field before exposing them to end users or using them in production workflows.

## Next steps

- See [Using the Languages API](/docs/languages/using-the-languages-api) for additional query patterns and examples
- Review the [API reference for `GET /v3/languages`](/api-reference/languages/retrieve-languages-by-resource) and [`GET /v3/languages/resources`](/api-reference/languages/retrieve-resources)
- If you're migrating from `/v2/languages`, see the [migration guide](/docs/languages/migrating-from-v2-languages)
177 changes: 177 additions & 0 deletions docs/voice/translate-a-pre-recorded-audio-file.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
---
title: "Translate a Pre-Recorded Audio File"
description: "Submit a pre-recorded audio file for translation, poll for results, and download translated transcripts or audio using the Voice Translate Job API."
covers: [Translate Audio Files]
---

The Voice Translate Job API translates pre-recorded audio files asynchronously. Unlike the real-time Voice API (which streams live audio over WebSocket), this API accepts an uploaded audio file and returns one or more translated outputs: plain text transcripts, SRT subtitles, or translated speech audio. Use it for podcasts, meeting recordings, video files, and other pre-recorded content.

This guide walks through the complete workflow: creating a job, uploading your audio file, polling for results, and downloading them.

## Prerequisites

- A DeepL API account with Voice Translate Job API access (currently closed alpha — contact your customer success manager to request access)
- `curl` for running the examples
- An audio file to translate (see [Supported Source Audio Formats](/api-reference/jobs-voice-translate/create-voice-translate-job#supported-source-audio-formats) for accepted formats)

## Overview of the workflow

Translating an audio file takes four steps:

1. Create a job to tell the API what outputs you want
2. Upload your audio file to the URL returned in step 1
3. Poll the job status until all targets are complete
4. Download each completed result

Steps 1 and 2 must happen within 5 minutes of each other. After all results are downloaded, the job is deleted.

<Tip>
API Free users should use `https://api-free.deepl.com` in place of `https://api.deepl.com` in all requests below.
</Tip>

## Step 1: Create a job

Send a POST request to `/v1/jobs/voice/translate` describing your source file and the outputs you want. You must declare the source file's `content_length` (in bytes) and `content_type` before uploading — the API uses these to prepare the upload URL.

This example translates an English MP3 into a German plain-text transcript and a Spanish PCM audio file:

```bash
curl -X POST https://api.deepl.com/v1/jobs/voice/translate \
-H "Authorization: DeepL-Auth-Key YOUR_AUTH_KEY" \
-H "Content-Type: application/json" \
-d '{
"source_file": {
"name": "podcast-episode-42.mp3",
"content_type": "audio/mpeg",
"content_length": 15728640
},
"parameters": {
"source_language": "en"
},
"targets": [
{ "language": "de", "type": "text/plain" },
{ "language": "es", "type": "audio/pcm;encoding=s16le;rate=16000" }
]
}'
```

A successful response returns `201` with a `job_id`, an `upload_url`, and a `signature`:

```json

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upload URL used verbatim in Step 2 curl example

The curl command in Step 2 hardcodes the full upload_url value from the Step 1 response example. This is fine for illustration, but the text before it says 'PUT your audio file directly to the upload_url' without making it explicit that the reader should substitute the actual URL returned in their response, not copy this literal URL. A brief note or variable placeholder would prevent copy-paste confusion.

Suggested change
```json
Replace `$UPLOAD_URL` with the `upload_url` from the step 1 response.
```bash
UPLOAD_URL="https://assets.deepl.com/collections/a74d88fb-ed2a-4943-a664-a4512398b994/assets/b1c2d3e4-f5a6-7890-abcd-ef1234567890"
SKIP

{
"job_id": "a74d88fb-ed2a-4943-a664-a4512398b994",
"signature": "eyJhbGciOiJIUzI1NiIs...",
"upload_url": "https://assets.deepl.com/collections/a74d88fb-ed2a-4943-a664-a4512398b994/assets/b1c2d3e4-f5a6-7890-abcd-ef1234567890"
}
```

Save the `job_id` — you'll need it to poll status and download results. The `upload_url` is where you send the audio file in the next step.

For all supported output types, see [Supported Output Formats](/api-reference/jobs-voice-translate/create-voice-translate-job#supported-output-formats). For available languages, see the reference page.

## Step 2: Upload the audio file

PUT your audio file directly to the `upload_url`. Set `Content-Type` to match the `content_type` you declared in step 1, and `Content-Length` to the exact byte size.

```bash
curl -X PUT "https://assets.deepl.com/collections/a74d88fb-ed2a-4943-a664-a4512398b994/assets/b1c2d3e4-f5a6-7890-abcd-ef1234567890" \
-H "Content-Type: audio/mpeg" \
-H "Content-Length: 15728640" \
--data-binary @podcast-episode-42.mp3
```

A successful upload returns `200` with an empty body. Processing begins automatically once the upload completes.

<Warning>
You have 5 minutes from job creation to complete the upload. If you miss this window, the job expires and you'll need to create a new one.
</Warning>

The `Content-Length` header must exactly match the `content_length` you declared when creating the job. A mismatch results in a `400` error.

## Step 3: Poll for results

GET the job status by `job_id`. Each target in the `results` array has its own `status` field and progresses independently.

```bash
curl https://api.deepl.com/v1/jobs/voice/translate/a74d88fb-ed2a-4943-a664-a4512398b994 \
-H "Authorization: DeepL-Auth-Key YOUR_AUTH_KEY"
```

While processing, the response looks like this:

```json
{
"job_id": "a74d88fb-ed2a-4943-a664-a4512398b994",

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Poll interval recommendation has no upper bound rationale

The guide recommends polling every 5-10 seconds but gives no guidance on when to stop if the job appears stuck or on maximum expected processing time. Developers building production integrations may implement infinite polling loops. A brief note on a reasonable timeout or maximum wait time would help.

Suggested fix: Add a sentence after 'Poll every 5-10 seconds.' such as: 'Processing time varies with file length; a 1-hour recording may take several minutes. If the job has not completed after 30 minutes, treat it as failed and file a support request.'

"operation": "translate",
"product": "voice",
"parameters": { "source_language": "en" },
"source_file": {
"name": "podcast-episode-42.mp3",
"content_type": "audio/mpeg",
"content_length": 15728640
},
"targets": [
{ "language": "de", "type": "text/plain" },
{ "language": "es", "type": "audio/pcm;encoding=s16le;rate=16000" }
],
"results": [
{ "status": "processing" },
{ "status": "processing" }
],
"created_at": "2026-10-01T01:03:03.444Z",
"updated_at": "2026-10-01T04:03:03.333Z"
}
```

Results are returned in the same order as the `targets` array from your create request.

Poll every 5-10 seconds. When a target's status changes to `complete`, its result object includes a `download_url` and a `signature`:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Step 4 audio file extension guidance is informal and incomplete

The note 'use a file extension that matches the output format (.wav for PCM, .mp3 for MPEG, and so on)' is informal guidance without a pointer to a format reference. 'And so on' leaves developers to guess for other formats like SRT. A link to the supported output formats list would make this actionable.

Suggested fix: Replace 'and so on' with a link: 'For a full list of output types and their conventional extensions, see Supported Output Formats.'


```json
{
"results": [

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Download expiry window (1 hour) and upload window (5 minutes) sourcing not cross-referenced to reference docs

The 1-hour download expiry and 5-minute upload window are stated as facts in the guide, but there is no cross-reference to a reference page or requirements page that confirms these limits. If the limits change, this guide may go stale. The 'Voice API Requirements' link in Handling Failures covers rate/size limits but may not cover these time windows.

Suggested fix: Add a parenthetical link after each time limit pointing to the canonical source, e.g. 'within 1 hour (see Voice API Requirements)' — or confirm the requirements page covers these windows and relies on it as the single source of truth.

{
"status": "complete",
"download_url": "https://assets.deepl.com/collections/a74d88fb/assets/c3d4e5f6",
"signature": "eyJhbGciOiJIUzI1NiIs..."
},
{
"status": "failed",
"error": { "message": "processing failed" }
}
]
}
```

Individual targets can reach `complete` or `failed` status independently. Don't wait for all targets before downloading completed ones — a job can have a mix of statuses at the same time.

For the full status lifecycle (`pending` → `uploaded` → `processing` → `complete`), see the [Get Job Status](/api-reference/jobs-voice-translate/get-voice-translate-job-status#result-status-lifecycle) reference.

## Step 4: Download results

GET each completed result from its `download_url`. No authentication header is required for the download — the URL itself is access-controlled.

```bash
curl -o german-transcript.txt \
"https://assets.deepl.com/collections/a74d88fb/assets/c3d4e5f6"
```

For audio targets, use a file extension that matches the output format (`.wav` for PCM, `.mp3` for MPEG, and so on).

<Warning>
Download results within 1 hour of the upload completing. After this window, results expire and the job is deleted. Once a result is marked `downloaded`, assets are queued for deletion.
</Warning>

## Handling failures

If a target's status is `failed`, the `error.message` field describes what went wrong. A failure on one target doesn't affect the others — you can still download any that reached `complete`.

If the source file itself is invalid (wrong format, corrupted, or over 3 hours long), all targets will fail. Source audio over 3 hours is rejected even if the file is under 1 GB. For longer recordings, split the source into separate files and submit each as its own job.

For rate limits, file size limits, and concurrent job limits, see [Voice API Requirements](/docs/voice/voice-api-requirements).

## Next steps

- Check [Supported Source Audio Formats](/api-reference/jobs-voice-translate/create-voice-translate-job#supported-source-audio-formats) and [Supported Output Formats](/api-reference/jobs-voice-translate/create-voice-translate-job#supported-output-formats) for the full list of accepted inputs and producible outputs
- See the [Create Job](/api-reference/jobs-voice-translate/create-voice-translate-job) and [Get Job Status](/api-reference/jobs-voice-translate/get-voice-translate-job-status) endpoint references for complete request and response schemas
- For live audio, see the [Real-Time Voice Quickstart](/docs/voice/real-time-voice-quickstart)