Skip to content
Open
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 @@ -102,6 +102,9 @@
}
]
}
],
"pages": [
"docs/languages/query-language-support-for-a-specific-resource"
]
},
{
Expand Down Expand Up @@ -149,7 +152,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-an-audio-file-with-the-voice-translate-job-api"
]
},
{
Expand Down
192 changes: 192 additions & 0 deletions docs/languages/query-language-support-for-a-specific-resource.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
---
title: "Query language support for a specific resource"
description: "Use the v3/languages endpoint to look up which languages and features are available for a specific DeepL API resource before you make translation requests."
covers: [Languages]
---

The `/v3/languages` endpoint tells you which languages a given DeepL API resource supports and which optional features (formality, glossaries, tag handling, and more) are available per language. Calling it before you build language selectors or validate user input lets you stay current as DeepL adds languages, without maintaining a hardcoded list.

This guide shows you how to query language support for a resource, read the response, and check whether a specific feature is available for a language pair.

<Info>
The `resource` parameter is required. If you're migrating from `/v2/languages`, see the [migration guide](/docs/languages/migrating-from-v2-languages) — the v3 response structure is different.
</Info>

## Before you start

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 placement slightly delays key migration context

The Info callout about the required resource parameter and v2 migration appears after the opening two paragraphs. Developers who are migrating are likely to miss it since they may skim past the intro. The style guide recommends front-loading key information.

Suggested fix: Move the Info box to immediately after the title/opening sentence, before the second paragraph that begins 'This guide shows you...' — or fold the migration note into the Before you start section as a brief sentence with the link.


You need a DeepL API key. Find yours on the [API Keys & Limits page](https://www.deepl.com/your-account/keys). If you're on the Free plan, use `https://api-free.deepl.com` instead of `https://api.deepl.com` in all requests below.

## Step 1: Choose your resource

The `resource` parameter identifies which DeepL API product you're querying language support for. Choose the value that matches what you're building:

| **`resource` value** | **What it covers** |
|---|---|
| `translate_text` | Text translation via `/v2/translate` |
| `translate_document` | Document translation via `/v2/document` |
| `glossary` | Glossary management via `/v2/` and `/v3/glossaries` |
| `voice` | Speech transcription and translation via `/v3/voice` |
| `write` | Text improvement via `/v2/write` |
| `style_rules` | Style rules via the style rules endpoints |
| `translation_memory` | Translation memory features |

## Step 2: Fetch supported languages

Call `GET /v3/languages` with your chosen `resource` value. This example queries languages for text translation:

```sh

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.

Table left-alignment not specified for status/symbol columns

The resource table uses default alignment (no explicit column alignment syntax). Per CLAUDE.md, text columns should be left-aligned. The table currently has no alignment row specified, which may render inconsistently depending on Mintlify defaults.

Suggested fix: Add explicit column alignment to the table: |:---|:---| on the separator row to left-align both columns.

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

The response is an array where each object represents one language:

```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.

BCP 47 link points to an internal docs path but uses a full URL pattern

The BCP 47 link (https://developers.deepl.com/docs/resources/language-release-process) uses an absolute URL to an internal page. Per CLAUDE.md, internal links should use relative paths.

Suggested fix: Replace the absolute URL with a relative path: [BCP 47](/docs/resources/language-release-process).

A few things to notice in this response:

- `en` is source-only (`usable_as_source: true`, `usable_as_target: false`). For target languages, use a regional variant like `en-US` or `en-GB`.
- Language codes follow [BCP 47](https://developers.deepl.com/docs/resources/language-release-process). Don't assume codes are always two letters — treat them as opaque identifiers.
- The `features` object lists optional capabilities available for that language with this resource. A feature's absence means it isn't supported.

## Step 3: Filter source and target languages

Use `usable_as_source` and `usable_as_target` to build your language selectors:

```python
import requests

def get_languages(resource, auth_key):
response = requests.get(
"https://api.deepl.com/v3/languages",
params={"resource": resource},
headers={"Authorization": f"DeepL-Auth-Key {auth_key}"},
)
response.raise_for_status()
return response.json()

languages = get_languages("translate_text", auth_key="[yourAuthKey]")

source_languages = [lang for lang in languages if lang["usable_as_source"]]
target_languages = [lang for lang in languages if lang["usable_as_target"]]

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 response block missing request context

The JSON response block for GET /v3/languages/resources (lines ~115-129) is not preceded by a labeled 'Example response' heading or comment, making it easy to misread as a continuation of the curl request output. The two blocks (curl and JSON) are separated only by a blank line with no label.

Suggested fix: Add a short label between the curl block and the JSON block, e.g. 'The response lists each resource and its feature requirements:' to mirror the pattern used in Step 2.

print("Source languages:", [lang["lang"] for lang in source_languages])
print("Target languages:", [lang["lang"] for lang in target_languages])
```

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

Some features, like formality, depend on both the source and target language supporting it. To know which language must support a feature for it to be available, call `GET /v3/languages/resources`:

```sh
curl -X GET 'https://api.deepl.com/v3/languages/resources' \
--header 'Authorization: DeepL-Auth-Key [yourAuthKey]'
```

```json
[
{
"name": "translate_text",
"features": [
{ "name": "formality", "needs_target_support": true },
{ "name": "glossary", "needs_source_support": true, "needs_target_support": true },
{ "name": "tag_handling", "needs_source_support": true, "needs_target_support": true },
{ "name": "auto_detection", "needs_source_support": true }
]
}
]
```

For `formality`, only `needs_target_support` is set. This means you only need to check whether the target language's `features` object contains `formality` — the source language doesn't matter.

For `glossary`, both `needs_source_support` and `needs_target_support` are set. Both languages in the pair must support `glossary` for you to use a glossary on that translation.

Here's a helper that combines both calls to check whether a feature is available for a given pair:

```python
def is_feature_available(feature_name, source_lang, target_lang, resource_name, languages, resources):
# Find the feature's requirements from the resources list
resource_info = next(r for r in resources if r["name"] == resource_name)
feature_req = next(
(f for f in resource_info["features"] if f["name"] == feature_name),
None,
)
if feature_req is None:
return False # Feature not defined for this resource

lang_map = {lang["lang"]: lang for lang in languages}

if feature_req.get("needs_source_support"):
source = lang_map.get(source_lang, {})
if feature_name not in source.get("features", {}):
return False

if feature_req.get("needs_target_support"):
target = lang_map.get(target_lang, {})
if feature_name not in target.get("features", {}):
return False

return True

# Example: can we use a glossary for EN → DE?
available = is_feature_available("glossary", "en", "de", "translate_text", languages, resources)
print(f"Glossary available for EN→DE: {available}") # True
```

## Including beta languages

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

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

Beta languages appear in the response with `"status": "beta"`. Use the `status` field to decide whether to surface them to end users or restrict them to internal testing.

<Warning>
Don't hardcode the list of languages returned by this endpoint. New languages are added regularly — call the endpoint at startup (or on a schedule) and cache the result rather than maintaining a static list. See the [language release process](/docs/resources/language-release-process) for details on how DeepL introduces new languages.
</Warning>

## Next steps

- See the full API reference: [Retrieve languages](/api-reference/languages/retrieve-languages-by-resource) and [Retrieve language resources](/api-reference/languages/retrieve-resources)
- Browse all languages the API currently supports: [Languages supported](/docs/getting-started/supported-languages)
- If you're coming from `/v2/languages`: [Migrating from v2/languages](/docs/languages/migrating-from-v2-languages)
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
---
title: "Translate an Audio File with the Voice Translate Job API"
description: "Submit a pre-recorded audio file for translation and download the results using the asynchronous Voice Translate Job API."
covers: [Translate Audio Files]
---

The Voice Translate Job API translates pre-recorded audio files asynchronously. You submit a job, upload your audio file, poll for results, and download each output when it's ready. This guide walks through all four steps with a complete curl example.

Use this API for batch or offline workloads: podcasts, meeting recordings, video files, and similar pre-recorded content. For live audio, use the [real-time Voice API](/docs/voice/overview).

<Warning>
This API is only available to select DeepL customers and may change without notice. See [alpha and beta features](/docs/resources/alpha-and-beta-features) for details, or contact your customer success manager to request access.
</Warning>

## Prerequisites

- A DeepL API account with Voice Translate Job API access
- Your DeepL API key (set as `DEEPL_API_KEY` in the examples below)
- A pre-recorded audio file in a [supported format](/api-reference/jobs-voice-translate/reference#supported-source-audio-formats)

For file size, duration, concurrency, and supported format constraints, see the [Translate Audio Files reference](/api-reference/jobs-voice-translate/reference#limits).

API Pro users call `https://api.deepl.com`. API Free users call `https://api-free.deepl.com` instead.

## The four-step workflow

Every translation follows the same pattern:

1. **Create a job** to declare your source file and desired outputs. The API returns an upload URL.
2. **Upload your audio file** directly to that URL.
3. **Poll for status** until all targets are complete (or failed).
4. **Download each result** from its download URL.

The sections below walk through each step.

## Step 1: Create a job

Send a POST request to `/v1/jobs/voice/translate` with the source file metadata and your desired translation targets.

The `content_length` and `content_type` fields in `source_file` must match your actual file exactly. The API uses them to provision a pre-signed upload URL, so mismatches will cause the upload in step 2 to fail.

```bash
curl -X POST "https://api.deepl.com/v1/jobs/voice/translate" \
-H "Authorization: DeepL-Auth-Key $DEEPL_API_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 includes a `job_id`, an `upload_url`, and a `signature`:

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

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 expiry time limit inline in guide

The sentence 'You have 5 minutes to complete the upload before the URL expires' is a numeric limit that belongs on the reference page, not in the how-to guide.

Suggested change
"upload_url": "https://assets.deepl.com/collections/a74d88fb-ed2a-4943-a664-a4512398b994/assets/b1c2d3e4-f5a6-7890-abcd-ef1234567890"
"upload_url": "https://assets.deepl.com/collections/a74d88fb-ed2a-4943-a664-a4512398b994/assets/b1c2d3e4-f5a6-7890-abcd-ef1234567890"
}

Save the job_id for polling and the upload_url for the next step. Complete the upload before the URL expires — see the Translate Audio Files reference for the expiry window.

}
```

Save the `job_id` for polling and the `upload_url` for the next step. You have 5 minutes to complete the upload before the URL expires.

Each job can have multiple targets in different output types, so a single English recording can produce a German transcript, French subtitles, and Spanish audio in one request. See the [Translate Audio Files reference](/api-reference/jobs-voice-translate/reference) for the full list of output types and supported languages.

## Step 2: Upload your audio file

PUT your audio file to the `upload_url` returned in step 1. Set `Content-Type` to the same value you declared in `source_file.content_type`.

Do not include your DeepL API key in the upload request. The URL is pre-signed and only requires the `Content-Type` header. Adding an `Authorization` header will cause the request to fail.

```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" \
--data-binary @podcast-episode-42.mp3
```

A `200 OK` response with no body means the upload succeeded. Processing starts automatically once the file is received.

## Step 3: Poll for status

Check the job status by sending a GET request to `/v1/jobs/voice/translate/{job_id}`. Each target in the `results` array has its own `status` field that progresses independently.

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

While targets are still processing, the response looks like this:

```json
{
"job_id": "a74d88fb-ed2a-4943-a664-a4512398b994",
"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"
}
```

When a target completes, its result entry includes a `download_url` and `signature`:

```json
{
"results": [
{
"status": "complete",

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.

Polling guidance mixes how-to and reference content

The paragraph 'Poll every 10-30 seconds for files under 100 MB...' introduces numeric thresholds (100 MB, 10-30 seconds, several minutes) that are reference-level constraints. Per the API reference conventions in CLAUDE.md, numeric limits belong on the reference page, not in guides. The guide should give actionable polling advice without encoding specific limits inline.

Suggested fix: Replace with: 'Poll every 10-30 seconds. For polling interval and file size guidance, see the Translate Audio Files reference. Keep polling until every result has reached a terminal status: complete, failed, or downloaded.' Remove the '100 MB' and 'several minutes' mentions.

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

Results are returned in the same order as the `targets` array in your create request, so `results[0]` corresponds to `targets[0]`.

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.

Inline time limit for download window is a numeric constraint

The sentence 'You have 1 hour from the time the upload completes to download all results' encodes a specific numeric limit inline in a how-to guide. Per CLAUDE.md conventions, numeric limits live on the reference/requirements page, not in guide prose.

Suggested change
Results are returned in the same order as the `targets` array in your create request, so `results[0]` corresponds to `targets[0]`.
Results are returned in the same order as the `targets` array in your create request, so `results[0]` corresponds to `targets[0]`.


Poll every 10-30 seconds for files under 100 MB. Larger files or longer recordings may take several minutes. Keep polling until every result has reached a terminal status: `complete`, `failed`, or `downloaded`.

## Step 4: Download results

For each target with `status: complete`, download the result from its `download_url`. No authentication header is needed — the URL is pre-signed.

```bash
# Download the German plain-text transcript
curl -o translation_de.txt \
"https://assets.deepl.com/collections/a74d88fb/assets/c3d4e5f6"
```

After you download a result, its status transitions to `downloaded` and the asset is queued for deletion. You have 1 hour from the time the upload completes to download all results. After that window, or once all results are downloaded, the job is deleted and returns `404`.

## Handling partial failures

Individual targets can fail while others succeed. Check each result's `status` independently and handle `failed` results by reading the `error.message` field. You cannot retry a failed target within an existing job; you need to create a new job.